Compare commits

...
89 Commits
Author SHA1 Message Date
PIGCLOUD a6d0705e40 fix(upms): validate department switch authorization
Restrict department switching to the current user department list and reuse the localized access-denied message. Add regression coverage for valid switching and unauthorized or missing departments.

Refs #298

(cherry picked from commit b897c6a34f5f0ff3a4670eb9881e058b56de376d)
2026-11-06 15:19:40 +08:00
PIGCLOUD c0034f3799 Merge branch 'master' of github.com:pig-dev-team/pigx 2026-11-05 16:40:28 +08:00
PIGCLOUD db23fd2d0c feat(SysUserServiceImpl): 添加短信验证码校验逻辑,防止重复使用确保验证码一次性消费
- 更新用户注册和找回密码逻辑,使用新的验证码校验方法
- 添加 SysUserSmsCodeTests 测试类,验证验证码消费行为
2026-11-05 16:36:32 +08:00
PIGCLOUD fd5cd572a4 feat(SysRegisterController): 添加找回密码功能开关控制
- 在 SysRegisterController 中添加对找回密码功能的开关控制
- 如果系统已关闭找回密码功能,则返回失败信息
2026-11-05 16:36:04 +08:00
PIGCLOUD ab91447941 fix(flow): 转义 process.settings 列名,修复 jsqlparser 5.4 解析异常
jsqlparser 5.4 将 settings 视为关键字,TenantLineInnerInterceptor
解析含该列的 SQL 时抛出 ParseException。
2026-11-05 15:18:47 +08:00
PIGCLOUD e3440353ae fix(flow): 转义 process.settings 列名,修复 jsqlparser 5.4 解析异常
jsqlparser 5.4 将 settings 视为关键字,TenantLineInnerInterceptor
解析含该列的 SQL 时抛出 ParseException。
2026-11-05 15:18:43 +08:00
PIGCLOUD ff98dcc3fb chore(deps): 升级积木报表至 2.5.6.1 并覆盖 fastjson 版本 2026-11-05 13:05:57 +08:00
PIGCLOUD 467a1fea1b build(deps): 升级 MyBatis Plus 并适配新版 API
(cherry picked from commit 290764e739fa093eb8187ecd0f9a30062af95f8e)
2026-11-04 21:01:49 +08:00
PIGCLOUD 8d37e48d1f feat(flow): 删除旧的流程通知表和合并表的迁移脚本
- 删除了 `20261014_flow_first_batch.sql` 和 `20261014_flow_merge_tables.sql` 文件
- 更新 `FlowNoticeStatusEnum`,新增 REMOVING 状态
- 修改 `FlowNotice` 实体,确保在插入时保留 userId 值
- 在 `FlowDurableConfiguration` 中添加事务提交后撤销失效通知的逻辑
- 更新 `IFlowNoticeService` 接口,调整通知事件的处理逻辑
- 实现 `FlowNoticeServiceImpl` 中的通知撤销逻辑,确保不误删新通知
- 新增 `FlowMessageRemovalDTO` 数据传输对象,处理通知撤销请求
- 更新 `SysMessageService` 和 `SysMessageController`,支持内部调用撤销流程通知
- 添加 `SysMessageDeletionTests` 测试用例,验证撤销逻辑的正确性
2026-11-03 18:57:11 +08:00
PIGCLOUD 589d17f0d3 fix(db): 补齐国际化与导出权限并同步 Vue 生成模板 2026-11-03 16:50:12 +08:00
PIGCLOUD 5f4d9d6621 style(db): remove trailing blank line after flow sync 2026-11-03 15:24:25 +08:00
PIGCLOUD d6adbfa36e fix(build): upgrade Maven Surefire to 3.6.1
(cherry picked from commit 54dc3b330c245fc79428e4e216b66e405bd47e3f)
2026-11-03 15:22:56 +08:00
PIGCLOUD b003a1b444 fix(auth): 隔离验证码缓存并强制校验短信登录凭据
(cherry picked from commit 9468a3efae7c9b678d074697eced50a175bea1f6)
2026-11-03 14:56:26 +08:00
PIGCLOUD 0974a56294 feat(flow): 优化拒绝处理逻辑,支持多实例结束后重定向到发起人
- 更新 ApproveServiceTask 中拒绝处理逻辑,支持多实例结束后当前执行流尚未落库的情况
- 增加对 CommandContextUtil 的使用,确保执行流迁移的正确性
- 添加 ModelUtilMultiMatchTests 中的单元测试,验证拒绝到发起人的逻辑

(cherry picked from commit 9e37864b02b14a2c980b74e01e126b5d38d10b09)
2026-11-03 14:56:21 +08:00
PIGCLOUD 2f089f029f feat(flow): 条件分支支持多选(包容网关)
- 条件分支节点新增 multiMatch,开启后分叉与合并网关改为 InclusiveGateway,未开启保持排他网关
- 驳回到发起人时收回实例下全部执行流,修复并行、多选分支内驳回后兄弟分支残留导致汇聚异常
- 新增 H2 引擎测试覆盖命中多条、兜底、空分支、嵌套条件及分支内驳回

(cherry picked from commit b982714e1875632fde3338bc69f27052cad98a9d)
2026-11-03 14:56:21 +08:00
PIGCLOUD 93c52e41dc feat(flow): 添加打印模板导入逻辑
- 仅在导入流程时携带打印模板,编辑或复制时保留原模板

(cherry picked from commit ebb2cf463da55efa9ac64bc011ed7ffc8606578a)
2026-11-03 14:56:21 +08:00
PIGCLOUD 2053cc5766 feat(flow): 更新超时任务和通知扫描的延迟时间
- 将 scanReminders 和 scanNotices 方法的扫描延迟时间从 10000ms 修改为 60000ms
- 提高了超时任务和通知的处理频率,优化了系统性能

(cherry picked from commit 71c5177913e5eaf452f62387947be13a6f9cfcc6)
2026-11-03 14:56:21 +08:00
PIGCLOUD ddfc2b41d9 feat(flow): 支持节点 REST 表单权限解析
(cherry picked from commit 80459350acfcd41351497959884307dfa6bbf9c0)
2026-11-03 14:56:21 +08:00
PIGCLOUD 14cb5dfe0a feat(pigx-common): 更新 nacos 版本至 3.3.0
- 将 pigx-common 模块中的 nacos.client.version 更新至 3.3.0
- 将 pigx-register 模块中的 nacos.version 更新至 3.3.0

(cherry picked from commit a25fcc1dedd49b73808fd4c3345da220b2bc50d8)
2026-11-03 14:56:21 +08:00
PIGCLOUD f8742f5e69 fix(flow): 移除回调持久化重试并限制单次通知并发
(cherry picked from commit f7cd6724ee5e5a2fbb7a677f386e582ba9d92610)
2026-11-03 14:56:16 +08:00
PIGCLOUD e0d43f41a1 refactor(flow): migrate durable workspace services to MyBatis Plus
(cherry picked from commit 1b0f4b664cb310fe236dfb046bef9c8248543d71)
2026-11-03 14:56:16 +08:00
PIGCLOUD b04588ffae fix: 修复安全过滤链顺序并清理构建告警
(cherry picked from commit 7dfeb3b7ed6b44b12f72cc82aed92a38e9d39af0)
2026-11-03 14:55:55 +08:00
PIGCLOUD fbb5bd121c feat(api): 删除不再使用的 ConnectConstants、SysDeptRelation、CpUserExcelVo、RoleVO 和 SysSystemSmsConfigVO 类文件
- 移除 ConnectConstants 接口及其相关常量定义
- 删除 SysDeptRelation 类,涉及部门关系表的定义
- 移除 CpUserExcelVo 类,涉及企业微信用户导入的定义
- 删除 RoleVO 类,涉及角色表的定义
- 移除 SysSystemSmsConfigVO 类,涉及系统短信配置的定义

(cherry picked from commit 7ef91170320ae5920458b6e0d64c97e0c6422f62)
2026-11-03 14:55:48 +08:00
PIGCLOUD 72ac720fb6 feat(codegen): 删除不再使用的 GenConfig、TableEntity、SqlDto、PayErrorCodes 等类文件
- 移除冗余的代码文件以简化项目结构
- 清理旧的工具类和实体类,提升代码可维护性

(cherry picked from commit f6eb09a169a2a1bd52501b543018aef15f74e21e)
2026-11-03 14:55:43 +08:00
PIGCLOUD cf917b5988 feat(flow): 新增流程回调投递、草稿存储及流程可靠性服务
- 新增 FlowCallbackDelivery 类,实现 HTTP 回调的至少一次投递机制
- 新增 FlowDraftService 类,支持个人流程草稿的保存、读取和删除
- 新增 FlowReliabilityService 类,处理流程通知、催办与回调的持久化
- 删除 FlowWorkspaceTests 测试类,整合测试逻辑至其他模块

(cherry picked from commit 07c8ccc0e845d30dcfaeeb6d01c999460093c8ac)
2026-11-03 14:55:33 +08:00
PIGCLOUD d0aa3a7284 fix(flow): unify workflow notices and draft reliability
(cherry picked from commit 1632fbb83d24be1523e6fa809ed2ad0ca3e6cb0b)
2026-11-03 14:55:33 +08:00
PIGCLOUD 4f6e796c55 chore(deps): upgrade dynamic-datasource to 4.6.0
(cherry picked from commit 7ff6b91a41cda697f52c3560b4202dab0626e0f9)
2026-11-03 14:55:20 +08:00
PIGCLOUD 085235710b feat(flow): 持久化流程草稿提醒与可靠回调
(cherry picked from commit f6871bee7c5cddaaa5650a284ac3cc3fec16dd18)
2026-11-03 14:55:15 +08:00
PIGCLOUD 749931b53e fix(flow): 按发布版本隔离流程节点配置
(cherry picked from commit 86ae98ad5b29a6d20b7e44283549d7337bb961df)
2026-11-03 14:55:00 +08:00
PIGCLOUD a177f3073a feat(app-article, app-article-category, sys-post): 添加文章和分类拖拽排序功能
- 在 AppArticleController 和 AppArticleCategoryController 中新增排序接口
- 在 AppArticleService 和 AppArticleCategoryService 中添加排序方法
- 在 SysPostController 和 SysPostService 中实现岗位排序功能
- 所有排序功能支持按传入 ID 顺序调整,保留未选中记录位置

(cherry picked from commit 0ba00ddcaba9a042bf082c10230042ba0ecf84d2)
2026-11-03 14:54:51 +08:00
PIGCLOUD d927a5b984 feat(pom): 更新 aws.sdk.version 和 git.commit.version 依赖版本
- 将 aws.sdk.version 从 2.44.14 更新至 2.55.3
- 将 git.commit.version 从 4.9.9 更新至 10.0.1

(cherry picked from commit 9072124412e5961d5ff87adc8d3d8839704b5c05)
2026-11-03 14:54:45 +08:00
PIGCLOUD daad35902e feat(pigx-codegen): 新增JDBC URL安全校验器,防止危险参数注入
- 新增JdbcUrlSecurityValidator,支持MySQL、PostgreSQL、HighGo、SQL Server、Oracle、DB2、DM的JDBC URL危险参数检测
- 在GenDatasourceConfServiceImpl中保存数据源配置时进行安全校验,拒绝包含不安全参数的连接
- 新增GenDatasourceConfServiceImplTests测试用例,覆盖安全与安全的URL场景
- 更新AGENTS.md,调整构建命令描述

(cherry picked from commit cabe0775957e6274cf3c31760724a5ee87fdd743)
2026-11-03 14:54:37 +08:00
PIGCLOUD e4bda833bc docs(db): remove unavailable workflow migration reference 2026-11-03 14:51:50 +08:00
PIGCLOUD cbdd0e202c fix(xss): adapt safelist override to updated jsoup 2026-11-03 14:51:50 +08:00
PIGCLOUD ccdbde5229 fix(pigx-common-security): 修复内部请求校验可被伪造 IP 绕过的问题
- 改用 request.getRemoteAddr() 获取客户端地址,避免从可自定义的转发头提取 IP
- 仅放行 IPv4 回环地址,未携带内部标记的外部请求交由后续认证鉴权处理

(cherry picked from commit b5e5b837ef3e82c4d8231be0bcbd6641c3de7516)
2026-11-03 14:49:33 +08:00
PIGCLOUD 3b15ea4235 fix(auth): 限制表单登录仅使用密码认证
(cherry picked from commit d3c470a848b5369459cd2acc9662276ed52b0e15)
2026-11-03 14:49:32 +08:00
PIGCLOUD 3fd14644cd fix(sentinel): 脱敏数据访问异常并保留异常上报
(cherry picked from commit 194b2d10a8210e93874ad4d1789cf0a4fa4889bd)
2026-11-03 14:49:29 +08:00
PIGCLOUD 4471c363cb feat(controller): 为获取客户端详情和导出功能添加权限控制
- 在 getByClientId 方法上添加 @HasPermission 注解,限制访问权限
- 在 export 方法上添加 @HasPermission 注解,限制访问权限

(cherry picked from commit b84bbffd6e4e74c129d5d02bc2fa3809340dddb7)
2026-11-03 14:49:29 +08:00
PIGCLOUD c0a2b97679 feat(pom): 更新 Spring Boot 和 Spring Cloud 版本
- 将 spring-boot.version 更新至 4.1.2
- 将 spring-cloud.version 更新至 2025.1.4

(cherry picked from commit be2fcf39c253c81826a2bb6266b9de6516d22680)
2026-11-03 14:49:28 +08:00
PIGCLOUD 45250841a5 fix: 调整 sys_route_conf 表 route_id 字段长度为 64
- 将 route_id 字段由 varchar(30) 调整为 varchar(64),避免路由 ID 超长导致存储异常

(cherry picked from commit f681502a5badfbc6bf73ee3c34423241c3b14337)
2026-11-03 14:49:28 +08:00
PIGCLOUD 57877b951f fix(flow): 将查询索引维护到原有初始化 SQL
(cherry picked from commit 88f042d1812902938bdcc0186466e43cc99c13da)
2026-11-03 14:49:27 +08:00
PIGCLOUD 618a29be52 perf(flow): 补充工作流列表查询索引
(cherry picked from commit 51d9242b291efd0ee4f4de329f2e6ccf88389014)
2026-11-03 14:49:27 +08:00
PIGCLOUD 67608f76ba fix(dependabot): 禁止 IJPay 自动升级
(cherry picked from commit efd55dffa7b9e826d894862cb466cf8837ce4e95)
2026-11-03 14:49:24 +08:00
PIGCLOUD 22f55b8ba3 fix(db): 更新初始化脚本注释以指明用途
- 添加注释说明工作流新装/存量升级入口
- 确保初始化脚本不用于升级已有数据库

(cherry picked from commit e2a00ee9c1264d25be641089f3729cac60e0f37f)
2026-11-03 14:49:24 +08:00
PIGCLOUD 9d5ff9c022 fix(message): allow users to delete their own notifications
(cherry picked from commit ea051ea5d76ee9e553754f027e6d3c5ce3aec2b1)
2026-11-03 14:49:23 +08:00
PIGCLOUD ec93f78417 feat(dependabot): 增加对 IJPay 小版本升级的支持
- 更新 dependabot 配置,允许 IJPay 依赖的小版本升级
- 修改注释以反映对 LangChain4j 和 IJPay 的支持

(cherry picked from commit 1b3df5325a1ae6516572e8937c5a56031fcd22ad)
2026-11-03 14:49:23 +08:00
PIGCLOUD 65f184ace6 fix(upms): 保存网站配置时保留各终端验证码开关
移除网站验证码模式变更时对所有 OAuth 终端 captcha_flag 的批量同步,保留前端对默认登录终端的同步行为。

Refs: pig/pigx#7631
(cherry picked from commit 836c2ea5e22f9e9a88f60d8a699e4a66e88979b9)
2026-11-03 14:49:22 +08:00
PIGCLOUD ca84985d63 feat(pigx-mp-platform): 新增按wxUserId精确过滤微信消息查询
- 分页查询新增 wxUserId 精确匹配条件,字段为空时不参与过滤
- 更新接口注释,明确各过滤字段的匹配规则
- 新增 WxFansMsgControllerTests 单元测试,验证按公众号和粉丝维度过滤

(cherry picked from commit 66870cc38f44ccc6c116a09c5e5620ac4b43f9b3)
2026-11-03 14:49:20 +08:00
PIGCLOUD b8fda524f8 fix(pigx-codegen): 修改代码生成默认分支为 master
- 将默认分支从 202601 调整为 master,避免生成代码时指向不存在的分支

(cherry picked from commit 0a86a2d9d012198c55ae77a423396ce709498703)
2026-11-03 14:49:20 +08:00
dependabot[bot] dc663491b6 chore(deps): bump org.jsoup:jsoup in /pigx-common/pigx-common-bom
Bumps [org.jsoup:jsoup](https://github.com/jhy/jsoup) from 1.15.4 to 1.23.2.
- [Release notes](https://github.com/jhy/jsoup/releases)
- [Changelog](https://github.com/jhy/jsoup/blob/master/CHANGES.md)
- [Commits](https://github.com/jhy/jsoup/compare/jsoup-1.15.4...jsoup-1.23.2)

---
updated-dependencies:
- dependency-name: org.jsoup:jsoup
  dependency-version: 1.23.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 7e3fd60373145503a28dc998f44b9467d3d7edc4)
2026-11-03 14:49:19 +08:00
PIGCLOUD cca41756c8 chore(deps): upgrade hutool to 5.8.48
(cherry picked from commit cc59d9bf0736cdc75affb25c4e070808da9ae0fd)
2026-11-03 14:49:19 +08:00
PIGCLOUD cb08f11a5d feat(wx-account-fans): 添加粉丝导出功能及数据脱敏处理
- 新增 WxAccountFansExcelVO 类用于粉丝数据的 Excel 导出
- 在 WxAccountFansController 中实现导出接口,支持按条件筛选粉丝
- 更新 WxAccountFansServiceImpl 中的粉丝更新逻辑,确保数据同步至微信
- 对粉丝的 OpenID 进行脱敏处理,确保隐私安全

(cherry picked from commit a277413d3d7681407daca46bbb635f57a032b25f)
2026-11-03 14:49:18 +08:00
PIGCLOUD e63dc8678e feat(excel-converters): 增加 WRITE_INSTANCE 的文档注释
- 为 WRITE_INSTANCE 添加了详细的文档注释,说明其在未指定目标单元格类型时的用途和返回值。

(cherry picked from commit 8bcff3a8a1879eac50527c25bb99ddc4b35b757d)
2026-11-03 14:49:17 +08:00
PIGCLOUD abf10a2a19 feat(excel-converters): 增强内置转换器功能,添加 StringArrayConverter.WRITE_INSTANCE
- 注册 StringArrayConverter.WRITE_INSTANCE 到 Excel 写构造器
- 优化代码格式,保持一致性

(cherry picked from commit 3800300f7f101560674dacc6a0840e279b753a2e)
2026-11-03 14:49:17 +08:00
PIGCLOUD 1db9bdc636 feat(pom): 添加跳过测试的配置
- 在项目属性中添加 `<skipTests>true</skipTests>` 配置,以便在构建时跳过测试。

(cherry picked from commit 1f1126001d2177aac7a3990d42f6c048f701d536)
2026-11-03 14:49:16 +08:00
PIGCLOUD 60babcbc3d fix: 为 JDK 25+ 添加 Byte Buddy 版本覆盖 profile
- 新增 jdk25-byte-buddy profile,在 JDK 25 及以上环境显式指定
  Byte Buddy 版本为 1.18.11,避免 Sentinel BOM 引入的旧版不兼容

(cherry picked from commit 07a61ad7e3a4b5f1f6e166557bbc6a36638d485b)
2026-11-03 14:49:16 +08:00
PIGCLOUD 2bc231f1bf feat(idempotent): 更新 IdempotentAutoConfiguration 和 IdempotentAutoConfigurationTests
- 移除冗余代码,简化 KeyResolver 和 RedissonClient 的配置
- 增强对 Redis 集群和哨兵模式的支持
- 更新测试用例以验证新的配置逻辑

(cherry picked from commit 0064a36a79cbd17c287eef358def60ea58bc9fbd)
2026-11-03 14:49:14 +08:00
PIGCLOUD 464c6df5b8 feat(sql): 更新 chat2bi_table_meta 表结构,调整字符集为 utf8mb4_general_ci
- 修改 chat2bi_table_meta 表的字符集和排序规则为 utf8mb4_general_ci
- 确保数据源相关字段的字符集一致性
2026-09-23 08:38:49 +08:00
PIGCLOUD 7720ea8547 feat(sql): 新增 chat2bi_table_meta 表结构,支持多数据源表元数据管理
- 包含数据源类型、ID、Schema名、表名等字段
- 提供表注释和列定义的 JSON 格式存储
- 增加多租户支持和同步时间字段

(cherry picked from commit f911168762486715c2c061134416c229462744ed)
2026-09-22 23:06:45 +08:00
PIGCLOUD ee27036339 chore: 统一 PIGX 项目版本为 6.2.0 2026-09-22 22:17:22 +08:00
PIGCLOUD db60b48f6c feat(db): 删除用户社交身份表及相关迁移脚本
- 删除 `20260922_user_social.sql` 文件及其内容
- 更新 `README.md`,移除关于用户社交身份迁移的说明
- 删除 `2026-03-18-flow-withdraw-design.md` 和 `2026-03-19-flow-withdraw-resubmit-design.md` 文档,清理与撤回和重新提交流程相关的设计文档
2026-09-22 22:14:02 +08:00
PIGCLOUD 7712a1bb3c fix(db): 补齐旧社交凭据迁移及升级说明 2026-09-22 22:09:47 +08:00
PIGCLOUD 816a63f0c2 fix(db): 对齐商业版初始化与增量升级表结构
参考 ai-dev-next c0b8a966b 的非 AI 表定义,统一单体与分库结构及内置 OAuth 客户端默认授权。保留 AI 表、菜单与路由排除边界。
2026-09-22 22:09:46 +08:00
PIGCLOUD 545b0a4642 refactor(pigx-upms-biz): 重构租户菜单缓存清理逻辑
- 将缩进从4个空格统一替换为Tab,并调整链式调用换行格式
- 重构 updateTenant 方法中菜单缓存清理逻辑,移除 RedisUtils 依赖,改用 Spring Cache 的 Cache.evict 按角色ID清除缓存
- 移除未使用的 RedisUtils 导入,增加 Cache 导入
- 微调日志格式和数组字面量格式

(cherry picked from commit b368a80a83a7d7efb34a63cda26e4ad64e99dfd0)
2026-09-22 21:52:17 +08:00
PIGCLOUD 1dc58c60cf feat(pigx-upms-biz): 增加飞书用户身份绑定功能
- 在 SysUserMapper.xml 中添加对飞书用户的查询支持
- 新增 FeishuLoginHandlerTests 测试类,验证飞书登录处理逻辑
- 测试覆盖飞书身份的代码交换和用户信息查询功能

(cherry picked from commit 2a0796cce45efd4e0444b0e2a6ef0a9e530dd226)
2026-09-22 21:52:04 +08:00
PIGCLOUD 75ec99389c fix(flow): 保留撤回终止原因
(cherry picked from commit ca125bd0ec08472c02c04356a5c4dd22965e3c78)
2026-09-22 21:51:48 +08:00
PIGCLOUD b6681ed578 fix(flow): 修正拒绝结束状态
(cherry picked from commit 5158476c3ff652f6a97256497074ba9cbd448ff5)
2026-09-22 21:51:48 +08:00
PIGCLOUD 53e7a6c2eb feat: 更新配置文件,增加openfeign请求压缩最小请求大小限制
(cherry picked from commit 0d6f12a2b934abe67eb7952e22ca7cdeddbd848f)
2026-09-22 21:51:48 +08:00
PIGCLOUD a810bf8f0b refactor(codegen): 移除 vform 无用代码
(cherry picked from commit 1bf57b916a66d61c6cfe52fdba827bb3c748c296)
2026-09-22 21:51:23 +08:00
PIGCLOUD 840732c52d refactor(pigx-upms): 重构社交登录,社交凭据迁移至独立表并移除码云/开源中国支持
- 将微信、企微、钉钉等社交登录凭据从 sys_user 字段迁移至独立的 sys_user_social 表
- 删除 Gitee 和 OSC 登录处理器及相关常量、枚举和字典项
- 更新绑定/解绑逻辑,使用 sys_user_social Mapper 进行增删改查
- 修改用户查询 SQL,通过 JOIN sys_user_social 表按社交标识查询用户
- 企业微信导入时自动写入社交绑定关系,并处理社交标识冲突异常
- 新增查询当前用户已绑定社交类型接口

(cherry picked from commit 2181b3311d62f980aecd6e144dbe7bce4e8dd8d2)
2026-09-22 21:49:55 +08:00
PIGCLOUD 6a4b8b1f1d Revert "fix(flow): 修正拒绝结束状态"
This reverts commit 4023b142d1.
2026-09-22 21:44:11 +08:00
PIGCLOUD 3f4d1dbf6e feat(sql): 新增 SqlSortUtils 工具类,提供排序字段校验功能
- 实现 isValidColumn 方法,校验前端传入的排序字段是否合法
- 更新 SqlFilterArgumentResolver,使用 SqlSortUtils 进行排序字段校验

(cherry picked from commit 1833847cd835d9972c2237cb804f00fd8075d4e8)
2026-09-22 21:43:41 +08:00
PIGCLOUD 39ea9a6eae feat(idempotent): 增强幂等配置,支持 Redis 集群和哨兵模式
- 新增对 Redis 集群和哨兵模式的支持
- 更新 Redisson 客户端配置逻辑
- 添加 IdempotentAutoConfigurationTests 测试类,验证新配置的正确性

(cherry picked from commit b836af6dc0dfbe17b20740cfda9e417deae1967d)
2026-09-22 21:43:41 +08:00
PIGCLOUD c80d5bc44a upgrade(jimu): 升级依赖版本并清理Nacos配置中的冗余项
- 升级 jimu 版本从 2.5.0 至 2.5.2,fastjson 版本从 2.0.58 至 2.0.65
- 清理 Nacos 配置(pigx-codegen-dev、pigx-daemon-quartz-dev、pigx-mp-platform-dev、pigx-jimu-platform-dev)中多余的 resources 配置项

(cherry picked from commit ad1b4945b9c1624217eee10d331a91a5004970f5)
2026-09-22 21:43:40 +08:00
PIGCLOUD 50b68563a6 feat: 更新nacos版本至3.2.4以增强功能和稳定性
(cherry picked from commit 52745112879f562cc745b3a8ec65603437c018c4)
2026-09-22 21:43:40 +08:00
PIGCLOUD 2a6ce673f5 fix(param): 将前端注册开关的默认值修改为关闭
(cherry picked from commit 4b94ca3fd4d7e2a5c69afbeb18fff8d336459c9d)
2026-09-22 21:43:39 +08:00
PIGCLOUD 08c081304a feat: 支持 JDK 25+ 显式声明 Lombok 注解处理器
- 添加 `jdk25-annotation-processor` profile,解决 JDK 25+ 下注解处理器自动发现关闭的问题

(cherry picked from commit 852cb195aa36425b3f0c512727a4f04b01da410a)
2026-09-22 21:43:39 +08:00
PIGCLOUD bca999535c feat: 更新 Spring Boot 和 Spring Cloud 版本至 4.1.1 和 2025.1.3
(cherry picked from commit 2d0eb6822807eaff80a491ddcaa0e584c0c197d6)
2026-09-22 21:43:39 +08:00
PIGCLOUD d46af13a2e feat(auth): 优化图形验证码生成逻辑,新增自定义颜色支持
(cherry picked from commit af900df10d6e9df4b9812332f126c2e96474722e)
2026-09-22 21:43:38 +08:00
PIGCLOUD 4023b142d1 fix(flow): 修正拒绝结束状态
(cherry picked from commit 5158476c3ff652f6a97256497074ba9cbd448ff5)
2026-09-22 21:43:38 +08:00
PIGCLOUD 269d7fe5e4 fix(quartz): 增加 REST 任务 URL 白名单校验
(cherry picked from commit ad6cd0600641ce916a09efe6affb56db97ad6890)
2026-09-22 21:43:37 +08:00
PIGCLOUD 2edc478f73 fix(codegen): 拦截非法表名
(cherry picked from commit 4e441f50473a4c3c5222002f23de6afdb5c70588)
2026-09-22 21:43:36 +08:00
PIGCLOUD cbb68a0e5b Merge remote-tracking branch 'origin/dev' into dev 2026-09-22 21:30:00 +08:00
PIGCLOUD 66e55f3258 refactor(pigx-common-datasource): 简化动态数据源配置逻辑
- 移除 DEFAULT_QUERY_DS_SQL 常量定义
- 直接使用传入的 queryDsSql 参数,简化条件判断逻辑
- 优化构造函数参数初始化方式
2026-03-20 11:32:36 +08:00
PIGCLOUD 9d198266d7 fix(db): 更新 config_info 表中的配置内容和 MD5 值 2026-03-20 11:28:17 +08:00
PIGCLOUD fe64bf38c2 refactor(pigx-common-datasource): 重构动态数据源配置
- 移除 DruidDataSourceProperties 配置类,直接使用 DruidDataSourceWrapper
- 移除冗余的配置属性和创建逻辑,提升代码简洁性
2026-03-20 11:28:11 +08:00
PIGCLOUD f759865202 feat(pigx-flow-biz): 新增流程撤回功能
- 在 ITaskService 接口中添加 withdrawProcessInstance 方法用于撤回流程实例
- 实现 TaskServiceImpl 中的撤回逻辑,包括权限校验和流程状态更新
- 新增 terminateProcessInstance 私有方法统一处理流程终止和撤回操作
- 在 ProcessInstanceRecordVo 中添加撤回相关字段 canWithdraw 和 withdrawDisabledReason
- 优化 ProcessInstanceServiceImpl 查询逻辑,批量处理驳回状态和撤回状态判断
- 在 ProcessTaskController 中添加撤回流程实例的接口端点
- 定义流程撤回相关的常量,包括结束原因和执行人状态
2026-03-19 08:41:55 +08:00
PIGCLOUD b553787cfd docs(flow): add withdraw resubmit design 2026-03-19 08:39:53 +08:00
PIGCLOUD 2c70387ace docs(flow): add starter withdraw design 2026-03-18 23:57:10 +08:00
397 changed files with 10455 additions and 7496 deletions

No files matched your search

+2
View File
@@ -17,6 +17,8 @@ updates:
update-types:
- version-update:semver-minor
ignore:
# 完全忽略 IJPay 的所有升级。
- dependency-name: "com.github.javen205:IJPay-*"
# 完全忽略 MyBatis-Plus BOM 的所有升级。
- dependency-name: "com.baomidou:mybatis-plus-bom"
# 完全忽略 MyBatis-Plus-Join 的所有升级。
+4 -8
View File
@@ -11,19 +11,15 @@ layout.
## Build, Test, and Development Commands
- `mvn -T 1C clean install -DskipTests` compiles all modules with the managed BOM; add `-pl pigx-gateway -am` to limit
the build to a single service.
- `mvn -pl pigx-gateway spring-boot:run` (swap the module name as needed) starts a service with dev profiles for live
reloads.
- `docker-compose up -d pigx-mysql pigx-redis pigx-register` brings up local infrastructure; run
`docker-compose up --build pigx-auth pigx-upms` when you need the auth stack.
- `mvn -pl pigx-knowledge test` executes module tests; append `-DskipITs` to bypass integration suites.
Focused Maven commands are allowed only for test execution. They must not be used to package, start, or restart
services.
## Testing Guidelines
Use `spring-boot-starter-test` (JUnit 5, AssertJ, Mockito) for unit and slice tests. Name classes `*Tests.java` and
colocate fixtures in `src/test/resources`. Target >70% line coverage on the service layer that guards authentication,
gateway filters, and approval flows. Run `mvn verify` before opening a PR to exercise the full plugin stack.
gateway filters, and approval flows. Before opening a PR, run the focused tests required by the change, then perform
the required runtime validation. Do not use `mvn verify` as a build shortcut unless the user explicitly requests it.
## Commit & Pull Request Guidelines
+326 -275
View File
@@ -1,3 +1,4 @@
-- 本文件仅用于初始化,不用于升级已有数据库。
-- pigx 单体模式的数据库【可选】
create database `pigxx_boot` default character set utf8mb4 collate utf8mb4_general_ci;
@@ -94,7 +95,7 @@ CREATE TABLE `sys_api_key` (
`update_by` varchar(64) DEFAULT NULL COMMENT '更新者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`tenant_id` bigint DEFAULT NULL COMMENT '租���ID',
`tenant_id` bigint DEFAULT NULL COMMENT '租户ID',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_api_key_hash` (`api_key_hash`),
KEY `idx_user_id` (`user_id`)
@@ -131,8 +132,8 @@ CREATE TABLE `sys_dept` (
`dept_id` bigint(20) NOT NULL COMMENT '部门ID',
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '部门名称',
`sort_order` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标志',
@@ -169,8 +170,8 @@ CREATE TABLE `sys_dict` (
`id` bigint(20) NOT NULL COMMENT '编号',
`dict_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '字典类型',
`description` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '描述',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remarks` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注信息',
@@ -228,8 +229,8 @@ CREATE TABLE `sys_dict_item` (
`description` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '字典项描述',
`list_class` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '标签类型',
`sort_order` int(11) NOT NULL DEFAULT '0' COMMENT '排序(升序)',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remarks` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注信息',
@@ -281,8 +282,6 @@ INSERT INTO `sys_dict_item` VALUES (33, 11, '0', '正常', 'status_type', '状
INSERT INTO `sys_dict_item` VALUES (34, 11, '9', '冻结', 'status_type', '状态冻结', NULL, 1, ' ', ' ', '2019-05-15 16:31:56', '2019-05-16 22:30:50', '状态冻结', '0', 1);
INSERT INTO `sys_dict_item` VALUES (35, 12, '1', '系统类', 'dict_type', '系统类字典', NULL, 0, ' ', ' ', '2019-05-16 14:20:40', '2019-05-16 14:20:40', '不能修改删除', '0', 1);
INSERT INTO `sys_dict_item` VALUES (36, 12, '0', '业务类', 'dict_type', '业务类字典', NULL, 0, ' ', ' ', '2019-05-16 14:20:59', '2019-05-16 14:20:59', '可以修改', '0', 1);
INSERT INTO `sys_dict_item` VALUES (37, 2, 'GITEE', '码云', 'social_type', '码云', NULL, 2, ' ', ' ', '2019-06-28 09:59:12', '2019-06-28 09:59:12', '码云', '0', 1);
INSERT INTO `sys_dict_item` VALUES (38, 2, 'OSC', '开源中国', 'social_type', '开源中国登录', NULL, 2, ' ', ' ', '2019-06-28 10:04:32', '2019-06-28 10:04:32', '', '0', 1);
INSERT INTO `sys_dict_item` VALUES (39, 14, 'password', '密码模式', 'grant_types', '支持oauth密码模式', NULL, 0, ' ', ' ', '2019-08-13 07:35:28', '2019-08-13 07:35:28', NULL, '0', 1);
INSERT INTO `sys_dict_item` VALUES (40, 14, 'authorization_code', '授权码模式', 'grant_types', 'oauth2 授权码模式', NULL, 1, ' ', ' ', '2019-08-13 07:36:07', '2019-08-13 07:36:07', NULL, '0', 1);
INSERT INTO `sys_dict_item` VALUES (41, 14, 'client_credentials', '客户端模式', 'grant_types', 'oauth2 客户端模式', NULL, 2, ' ', ' ', '2019-08-13 07:36:30', '2019-08-13 07:36:30', NULL, '0', 1);
@@ -341,6 +340,7 @@ INSERT INTO `sys_dict_item` VALUES (95, 29, '0', '黑名单', 'sensitive_type',
INSERT INTO `sys_dict_item` VALUES (96, 29, '1', '白名单', 'sensitive_type', '失败', NULL, 3, 'admin', ' ', '2023-10-27 10:30:47', NULL, NULL, '0', 1);
INSERT INTO `sys_dict_item` VALUES (97,2,'MINI','小程序登录','social_type','小程序登录',NULL,3,' ',' ','2022-02-18 13:56:25','2022-02-18 13:56:28',NULL,'0',1);
INSERT INTO `sys_dict_item` VALUES (98,2,'clarity','clarity','social_type','clarity',NULL,3,' ',' ','2022-02-18 13:56:25','2022-02-18 13:56:28',NULL,'0',1);
INSERT INTO `sys_dict_item` VALUES (100,2,'FEISHU','企业飞书','social_type','企业飞书扫码登录',NULL,5,' ',' ','2026-09-18 00:00:00','2026-09-18 00:00:00',NULL,'0',1);
INSERT INTO `sys_dict_item` VALUES (99,2,'umami','umami','social_type','umami',NULL,4,' ',' ','2026-06-23 00:00:00','2026-06-23 00:00:00',NULL,'0',1);
COMMIT;
@@ -358,8 +358,8 @@ CREATE TABLE `sys_file` (
`type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '文件类型',
`hash` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '文件hash',
`file_size` bigint(20) DEFAULT NULL COMMENT '文件大小',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT NULL COMMENT '上传时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标志',
@@ -391,9 +391,9 @@ CREATE TABLE `sys_i18n` (
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'name',
`zh_cn` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '中文',
`en` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '英文',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记',
PRIMARY KEY (`id`)
@@ -403,80 +403,83 @@ CREATE TABLE `sys_i18n` (
-- Records of sys_i18n
-- ----------------------------
BEGIN;
INSERT INTO `sys_i18n` VALUES (1, 'router.permissionManagement', '权限管理', 'Permission Management', '', '2023-02-14 02:03:59', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (2, 'router.userManagement', '用户管理', 'User Management', 'admin', '2023-02-14 10:39:08', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (3, 'router.menuManagement', '菜单管理', 'Menu Management', 'admin', '2023-02-15 23:14:39', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (4, 'router.roleManagement', '角色管理', 'Role Management', 'admin', '2023-02-15 23:15:51', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (5, 'router.departmentManagement', '部门管理', 'Department Management', 'admin', '2023-02-15 23:16:52', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (6, 'router.tenantManagement', '租户管理', 'Tenant Management', 'admin', '2023-02-24 10:08:29', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (7, 'router.postManagement', '岗位管理', 'Post Management', 'admin', '2023-02-24 10:12:58', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (8, 'router.systemManagement', '系统管理', 'System Management', 'admin', '2023-02-24 10:13:34', 'admin', '2023-02-24 10:58:30', '0');
INSERT INTO `sys_i18n` VALUES (9, 'router.operationLog', '操作日志', 'Operation Log', 'admin', '2023-02-24 10:14:47', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (10, 'router.dictManagement', '字典管理', 'Dictionary Management', 'admin', '2023-02-24 10:16:21', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (11, 'router.parameterManagement', '参数管理', 'Parameter Management', 'admin', '2023-02-24 10:17:04', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (12, 'router.codeGeneration', '代码生成', 'Code Generation', 'admin', '2023-02-24 10:19:16', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (13, 'router.terminalManagement', '终端管理', 'Terminal Management', 'admin', '2023-02-24 10:21:45', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (14, 'router.keyManagement', '密钥管理', 'Key Management', 'admin', '2023-02-24 10:22:52', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (15, 'router.tokenManagement', '令牌管理', 'Token Management', 'admin', '2023-02-24 10:23:22', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (16, 'router.quartzManagement', 'Quartz管理', 'Quartz Management', 'admin', '2023-02-24 10:24:32', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (17, 'router.metadataManagement', '元数据管理', 'Metadata Management', 'admin', '2023-02-24 10:25:11', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (1, 'router.permissionManagement', '权限管理', 'Permissions', '', '2023-02-14 02:03:59', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (2, 'router.userManagement', '用户管理', 'Users', 'admin', '2023-02-14 10:39:08', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (3, 'router.menuManagement', '菜单管理', 'Menus', 'admin', '2023-02-15 23:14:39', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (4, 'router.roleManagement', '角色管理', 'Roles', 'admin', '2023-02-15 23:15:51', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (5, 'router.departmentManagement', '部门管理', 'Departments', 'admin', '2023-02-15 23:16:52', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (6, 'router.tenantManagement', '租户管理', 'Tenants', 'admin', '2023-02-24 10:08:29', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (7, 'router.postManagement', '岗位管理', 'Positions', 'admin', '2023-02-24 10:12:58', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (8, 'router.systemManagement', '系统管理', 'System', 'admin', '2023-02-24 10:13:34', 'admin', '2023-02-24 10:58:30', '0');
INSERT INTO `sys_i18n` VALUES (9, 'router.operationLog', '操作日志', 'Operation Logs', 'admin', '2023-02-24 10:14:47', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (10, 'router.dictManagement', '字典管理', 'Dictionaries', 'admin', '2023-02-24 10:16:21', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (11, 'router.parameterManagement', '参数管理', 'Parameters', 'admin', '2023-02-24 10:17:04', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (12, 'router.codeGeneration', '代码生成', 'Code Generator', 'admin', '2023-02-24 10:19:16', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (13, 'router.terminalManagement', '终端管理', 'OAuth Clients', 'admin', '2023-02-24 10:21:45', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (14, 'router.keyManagement', '密钥管理', 'Keys', 'admin', '2023-02-24 10:22:52', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (15, 'router.tokenManagement', '令牌管理', 'Tokens', 'admin', '2023-02-24 10:23:22', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (16, 'router.quartzManagement', 'Quartz管理', 'Quartz Jobs', 'admin', '2023-02-24 10:24:32', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (17, 'router.metadataManagement', '元数据管理', 'Metadata', 'admin', '2023-02-24 10:25:11', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (18, 'router.documentExtension', '文档扩展', 'Document Extension', 'admin', '2023-02-24 10:27:23', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (19, 'router.fileManagement', '文件管理', 'File Management', 'admin', '2023-02-24 10:28:44', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (20, 'router.platformDevelopment', '开发平台', 'Platform Development', 'admin', '2023-02-24 10:29:28', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (21, 'router.dataSourceManagement', '数据源管理', 'Data Source Management', 'admin', '2023-02-24 10:30:33', 'admin', '2023-03-06 14:33:20', '0');
INSERT INTO `sys_i18n` VALUES (22, 'router.formDesign', '表单设计', 'Form Design', 'admin', '2023-02-24 10:31:33', 'admin', '2023-03-06 14:33:28', '0');
INSERT INTO `sys_i18n` VALUES (23, 'router.appManagement', 'APP管理', 'App Management', 'admin', '2023-02-24 10:33:22', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (24, 'router.customerManagement', '客户管理', 'Customer Management', 'admin', '2023-02-24 10:35:30', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (19, 'router.fileManagement', '文件管理', 'Files', 'admin', '2023-02-24 10:28:44', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (20, 'router.platformDevelopment', '开发平台', 'Development', 'admin', '2023-02-24 10:29:28', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (21, 'router.dataSourceManagement', '数据源管理', 'Data Sources', 'admin', '2023-02-24 10:30:33', 'admin', '2023-03-06 14:33:20', '0');
INSERT INTO `sys_i18n` VALUES (23, 'router.appManagement', 'APP管理', 'Apps', 'admin', '2023-02-24 10:33:22', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (24, 'router.customerManagement', '客户管理', 'Customers', 'admin', '2023-02-24 10:35:30', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (25, 'router.appRole', 'APP角色', 'App Role', 'admin', '2023-02-24 10:36:17', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (26, 'router.appPermission', 'APP权限', 'App Permission', 'admin', '2023-02-24 10:36:59', 'admin', '2023-02-24 10:37:47', '0');
INSERT INTO `sys_i18n` VALUES (27, 'router.appKey', 'APP秘钥', 'App Key', 'admin', '2023-02-24 10:36:59', 'admin', '2023-02-24 10:40:27', '0');
INSERT INTO `sys_i18n` VALUES (28, 'router.internationalizationManagement', '国际化管理', 'Internationalization Management', 'admin', '2023-02-24 10:36:59', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (29, 'router.auditLog', '审计日志', 'Audit Log', 'admin', '2023-02-24 10:36:59', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (27, 'router.appKey', 'APP秘钥', 'App Keys', 'admin', '2023-02-24 10:36:59', 'admin', '2023-02-24 10:40:27', '0');
INSERT INTO `sys_i18n` VALUES (28, 'router.internationalizationManagement', '国际化管理', 'Translations', 'admin', '2023-02-24 10:36:59', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (29, 'router.auditLog', '审计日志', 'Audit Logs', 'admin', '2023-02-24 10:36:59', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (30, 'router.systemMonitoring', '系统监控', 'System Monitoring', 'admin', '2023-02-24 10:36:59', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (31, 'router.generatePages', '生成页面', 'Generate Pages', 'admin', '2023-02-24 10:44:04', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (32, 'router.templateManagement', '模板管理', 'Template Management', 'admin', '2023-02-24 10:44:31', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (33, 'router.templateGroup', '模板分组', 'Template Group', 'admin', '2023-02-24 10:45:10', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (34, 'router.fieldManagement', '字段管理', 'Field Management', 'admin', '2023-02-24 10:46:04', 'admin', '2023-03-07 14:27:48', '0');
INSERT INTO `sys_i18n` VALUES (35, 'router.wechatPlatform', '公众号平台', 'WeChat Platform', 'admin', '2023-02-24 10:48:51', 'admin', '2023-02-24 11:03:41', '0');
INSERT INTO `sys_i18n` VALUES (36, 'router.accountManagement', '账号管理', 'Account Management', 'admin', '2023-02-24 10:13:34', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (37, 'router.menuSettings', '菜单设置', 'Menu Settings', 'admin', '2023-02-24 14:02:22', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (38, 'router.fanManagement', '粉丝管理', 'Fan Management', 'admin', '2023-02-24 14:03:44', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (39, 'router.messageManagement', '消息管理', 'Message Management', 'admin', '2023-02-24 14:03:45', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (40, 'router.paymentSystem', '支付系统', 'Payment System', 'admin', '2023-02-24 14:03:46', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (41, 'router.checkoutCounter', '收银台', 'Checkout Counter', 'admin', '2023-02-24 14:03:47', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (42, 'router.mediaManagement', '素材管理', 'Media Management', 'admin', '2023-02-24 14:03:48', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (43, 'router.paymentChannel', '支付渠道', 'Payment Channel', 'admin', '2023-02-24 14:03:49', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (44, 'router.productOrder', '商品订单', 'Product Order', 'admin', '2023-02-24 14:03:50', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (45, 'router.notificationRecord', '通知记录', 'Notification Record', 'admin', '2023-02-24 14:03:51', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (46, 'router.refundOrder', '退款订单', 'Refund Order', 'admin', '2023-02-24 14:03:52', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (47, 'router.paymentOrder', '支付订单', 'Payment Order', 'admin', '2023-02-24 14:03:53', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (48, 'router.autoReply', '自动回复', 'Auto Reply', 'admin', '2023-02-24 14:03:54', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (49, 'router.operationalData', '运营数据', 'Operational Data', 'admin', '2023-02-24 14:03:55', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (50, 'router.logManagement', '日志管理', 'Log Management', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (51, 'router.collaborativeOffice', '协同办公', 'Collaborative Office', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (32, 'router.templateManagement', '模板管理', 'Templates', 'admin', '2023-02-24 10:44:31', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (33, 'router.templateGroup', '模板分组', 'Template Groups', 'admin', '2023-02-24 10:45:10', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (34, 'router.fieldManagement', '字段管理', 'Fields', 'admin', '2023-02-24 10:46:04', 'admin', '2023-03-07 14:27:48', '0');
INSERT INTO `sys_i18n` VALUES (35, 'router.wechatPlatform', '公众号平台', 'WeChat', 'admin', '2023-02-24 10:48:51', 'admin', '2023-02-24 11:03:41', '0');
INSERT INTO `sys_i18n` VALUES (36, 'router.accountManagement', '账号管理', 'Accounts', 'admin', '2023-02-24 10:13:34', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (37, 'router.menuSettings', '菜单设置', 'Menus', 'admin', '2023-02-24 14:02:22', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (38, 'router.fanManagement', '粉丝管理', 'Followers', 'admin', '2023-02-24 14:03:44', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (39, 'router.messageManagement', '消息管理', 'Messages', 'admin', '2023-02-24 14:03:45', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (40, 'router.paymentSystem', '支付系统', 'Payments', 'admin', '2023-02-24 14:03:46', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (41, 'router.checkoutCounter', '收银台', 'Checkout', 'admin', '2023-02-24 14:03:47', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (42, 'router.mediaManagement', '素材管理', 'Media', 'admin', '2023-02-24 14:03:48', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (43, 'router.paymentChannel', '支付渠道', 'Channels', 'admin', '2023-02-24 14:03:49', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (44, 'router.productOrder', '商品订单', 'Product Orders', 'admin', '2023-02-24 14:03:50', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (45, 'router.notificationRecord', '通知记录', 'Notifications', 'admin', '2023-02-24 14:03:51', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (46, 'router.refundOrder', '退款订单', 'Refunds', 'admin', '2023-02-24 14:03:52', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (47, 'router.paymentOrder', '支付订单', 'Payment Orders', 'admin', '2023-02-24 14:03:53', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (48, 'router.autoReply', '自动回复', 'Auto Replies', 'admin', '2023-02-24 14:03:54', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (49, 'router.operationalData', '运营数据', 'Analytics', 'admin', '2023-02-24 14:03:55', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (50, 'router.logManagement', '日志管理', 'Logs', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (51, 'router.collaborativeOffice', '协同办公', 'Office', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (52, 'router.modelManagement', '模型管理', 'Model Management', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (53, 'router.modelDiagramView', '模型图查看', 'Model Diagram View', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (54, 'router.processManagement', '流程管理', 'Process Management', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (54, 'router.processManagement', '流程管理', 'Workflows', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (55, 'router.leaveWorkOrder', '请假工单', 'Leave Work Order', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (56, 'router.todoTask', '待办任务', 'Todo Task', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (57, 'router.tagManagement', '标签管理', 'Tag Management', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (58, 'router.articleInformation', '文章资讯', 'Article Information', ' ', '2023-08-10 13:40:09', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (59, 'router.articleCategory', '文章分类', 'Article Category', ' ', '2023-08-10 13:40:48', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (60, 'router.interfaceSettings', '界面设置', 'Interface Settings', ' ', '2023-08-10 13:41:21', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (62, 'router.cacheMonitoring', '缓存监控', 'Cache Monitoring', ' ', '2023-08-10 13:42:35', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (63, 'rotuer. initiateProcess', '发起流程', 'Initiate Process', ' ', '2023-08-10 13:44:23', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (64, 'router.taskManagement', '任务管理', 'Task Management', ' ', '2023-08-10 13:44:53', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (65, 'router.myInitiations', '我的发起', 'My Initiations', ' ', '2023-08-10 13:45:17', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (56, 'router.todoTask', '待办任务', 'Pending Tasks', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (57, 'router.tagManagement', '标签管理', 'Tags', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (58, 'router.articleInformation', '文章资讯', 'Articles', ' ', '2023-08-10 13:40:09', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (59, 'router.articleCategory', '文章分类', 'Categories', ' ', '2023-08-10 13:40:48', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (60, 'router.interfaceSettings', '界面设置', 'Page Settings', ' ', '2023-08-10 13:41:21', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (62, 'router.cacheMonitoring', '缓存监控', 'Cache Monitor', ' ', '2023-08-10 13:42:35', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (63, 'rotuer. initiateProcess', '发起流程', 'Start Workflow', ' ', '2023-08-10 13:44:23', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (64, 'router.taskManagement', '任务管理', 'Tasks', ' ', '2023-08-10 13:44:53', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (65, 'router.myInitiations', '我的发起', 'My Requests', ' ', '2023-08-10 13:45:17', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (66, 'router.copiedtoMe', '抄送给我', 'Copied to Me', ' ', '2023-08-10 13:45:46', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (67, 'router.completedTasks', '我的已办', 'Completed Tasks', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (68, 'router.bizPlatform', '业务平台', 'Biz Platform', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (69, 'router.baseTools', '基础工具', 'Base Tools', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (70, 'router.route', '路由管理', 'Route Management', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (71, 'router.datav', '大屏看板', 'Data Visual', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (72, 'router.bi', '数据报表', 'Bi Report', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (73, 'router.message', '信息推送', 'Message', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (74, 'router.sensitiveWords', '敏感词管理', 'Sensitive words', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (67, 'router.completedTasks', '我的已办', 'Handled Tasks', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (68, 'router.bizPlatform', '业务平台', 'Business', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (69, 'router.baseTools', '基础工具', 'Tools', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (70, 'router.route', '路由管理', 'Routes', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (71, 'router.datav', '大屏看板', 'Dashboards', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (72, 'router.bi', '数据报表', 'Reports', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (73, 'router.message', '信息推送', 'Message Push', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (74, 'router.sensitiveWords', '敏感词管理', 'Sensitive Words', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (75, 'router.clarityMonitoring', '站点统计', 'Site Analytics', 'admin', '2026-03-26 00:00:00', 'admin', NULL, '0');
INSERT INTO `sys_i18n` VALUES (128, 'router.siteConfig', '网站配置', 'Site Settings', 'admin', '2026-11-03 00:00:00', 'admin', NULL, '0');
INSERT INTO `sys_i18n` VALUES (129, 'router.sysArea', '行政区划', 'Admin Divisions', 'admin', '2026-11-03 00:00:00', 'admin', NULL, '0');
INSERT INTO `sys_i18n` VALUES (130, 'router.businessForm', '业务表单', 'Business Forms', 'admin', '2026-11-03 00:00:00', 'admin', NULL, '0');
INSERT INTO `sys_i18n` VALUES (131, 'router.databaseTables', '数据表管理', 'Database Tables', 'admin', '2026-11-03 00:00:00', 'admin', NULL, '0');
COMMIT;
-- ----------------------------
@@ -488,8 +491,8 @@ CREATE TABLE `sys_log` (
`log_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '日志类型',
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '日志标题',
`service_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '服务ID',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remote_addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '远程地址',
@@ -543,9 +546,9 @@ CREATE TABLE `sys_menu` (
`keep_alive` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '是否缓存,0否,1是',
`embedded` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '是否内嵌,0否,1是',
`menu_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '菜单类型,0:菜单 1:按钮',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标志,0未删除,1已删除',
`tenant_id` bigint(20) unsigned DEFAULT NULL COMMENT '租户ID',
@@ -703,6 +706,7 @@ INSERT INTO `sys_menu` VALUES (4011,'信息推送查看','sys_message_view',NULL
INSERT INTO `sys_menu` VALUES (4012,'信息推送新增','sys_message_add',NULL,NULL,4010,'1','1',1,'0',NULL,'1','',NULL,'admin','2023-10-25 14:52:00','0',1);
INSERT INTO `sys_menu` VALUES (4013,'信息推送修改','sys_message_edit',NULL,NULL,4010,'1','1',2,'0',NULL,'1','',NULL,'admin','2023-10-25 14:52:04','0',1);
INSERT INTO `sys_menu` VALUES (4014,'信息推送删除','sys_message_del',NULL,NULL,4010,'1','1',3,'0',NULL,'1','',NULL,'admin','2023-10-25 14:52:09','0',1);
INSERT INTO `sys_menu` VALUES (4015, '信息推送导出', 'sys_message_export', NULL, NULL, 4010, NULL, '1', 10, '0', '0', '1', 'admin', NOW(), 'admin', NOW(), '0', 1);
INSERT INTO `sys_menu` VALUES (5000,'支付系统',NULL,'/pay',NULL,9900,'iconfont icon-duoqudaozhifuxitongguanli','1',1,'0','0','0','admin','2023-02-27 10:57:14','admin','2023-11-27 14:52:11','0',1);
INSERT INTO `sys_menu` VALUES (5001,'收银台',NULL,'/biz/pay/cd/index',NULL,5000,'iconfont icon-shouyintai','1',0,'0','0','0','admin','2023-02-27 10:58:13','admin','2023-11-01 17:28:57','0',1);
INSERT INTO `sys_menu` VALUES (5002,'支付渠道',NULL,'/biz/pay/channel/index',NULL,5000,'iconfont icon-zhifuqudao','1',1,'0','0','0','admin','2023-02-27 19:36:55','admin','2023-11-01 17:29:01','0',1);
@@ -776,7 +780,6 @@ INSERT INTO `sys_menu` VALUES (7804,'删除','app_appContacts_del',NULL,NULL,780
INSERT INTO `sys_menu` VALUES (7805,'导入导出','app_appContacts_export',NULL,NULL,7800,NULL,'1',1,'0',NULL,'1','admin','2025-05-29 17:30:31',' ',NULL,'0',1);
INSERT INTO `sys_menu` VALUES (9000,'开发平台',NULL,'/gen',NULL,-1,'iconfont icon-DevOps','1',9,'0','0','0','','2019-08-12 09:35:16','admin','2025-01-26 22:38:09','0',1);
INSERT INTO `sys_menu` VALUES (9005,'数据源管理',NULL,'/gen/datasource/index',NULL,9000,'iconfont icon-shujuyuanguanli','1',0,'0',NULL,'0','','2019-08-12 09:42:11','admin','2025-01-26 22:26:51','0',1);
INSERT INTO `sys_menu` VALUES (9006,'表单设计',NULL,'/gen/design/index',NULL,9000,'iconfont icon-AIshiyanshi','0',2,'0','0','0','','2019-08-16 10:08:56','admin','2023-02-23 14:06:50','0',1);
INSERT INTO `sys_menu` VALUES (9007,'生成页面',NULL,'/gen/gener/index',NULL,9000,'iconfont icon-tongzhi4','0',1,'0','0','0','admin','2023-02-20 09:58:23','admin','2023-02-20 14:41:43','0',1);
INSERT INTO `sys_menu` VALUES (9050,'元数据管理',NULL,'/gen/metadata',NULL,9000,'iconfont icon-yuanshujuguanli','1',9,'0','0','0','','2018-07-27 01:13:21','admin','2023-02-23 19:55:10','0',1);
INSERT INTO `sys_menu` VALUES (9051,'模板管理',NULL,'/gen/template/index',NULL,9050,'iconfont icon-mti-mobanguanli','1',5,'0','0','0','admin','2023-02-21 11:22:54','admin','2023-02-23 19:56:03','0',1);
@@ -821,8 +824,8 @@ CREATE TABLE `sys_oauth_client_details` (
`additional_information` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '附加信息',
`autoapprove` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '自动授权',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记,0未删除,1已删除',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '所属租户ID',
@@ -833,11 +836,11 @@ CREATE TABLE `sys_oauth_client_details` (
-- Records of sys_oauth_client_details
-- ----------------------------
BEGIN;
INSERT INTO `sys_oauth_client_details` VALUES (1, 'app', NULL, 'app', 'server', 'password,refresh_token,authorization_code,client_credentials,mobile', 'http://localhost:4040/sso1/login,http://localhost:4041/sso1/login,http://localhost:8080/renren-admin/sys/oauth2-sso,http://localhost:8090/sys/oauth2-sso', NULL, 43200, 2592001, '{\"enc_flag\":\"1\",\"captcha_flag\":\"1\",\"online_quantity\":\"1\"}', 'true', '0', '', 'admin', NULL, '2023-02-09 13:54:54', 1);
INSERT INTO `sys_oauth_client_details` VALUES (1, 'app', NULL, 'app', 'server', 'password,refresh_token,authorization_code,mobile', 'http://localhost:4040/sso1/login,http://localhost:4041/sso1/login,http://localhost:8080/renren-admin/sys/oauth2-sso,http://localhost:8090/sys/oauth2-sso', NULL, 43200, 2592001, '{\"enc_flag\":\"1\",\"captcha_flag\":\"1\",\"online_quantity\":\"1\"}', 'true', '0', '', 'admin', NULL, '2023-02-09 13:54:54', 1);
INSERT INTO `sys_oauth_client_details` VALUES (2, 'daemon', NULL, 'daemon', 'server', 'password,refresh_token', NULL, NULL, 43200, 2592001, '{\"enc_flag\":\"1\",\"captcha_flag\":\"1\"}', 'true', '0', ' ', ' ', NULL, NULL, 1);
INSERT INTO `sys_oauth_client_details` VALUES (3, 'gen', NULL, 'gen', 'server', 'password,refresh_token', NULL, NULL, 43200, 2592001, '{\"enc_flag\":\"1\",\"captcha_flag\":\"1\"}', 'true', '0', ' ', ' ', NULL, NULL, 1);
INSERT INTO `sys_oauth_client_details` VALUES (4, 'mp', NULL, 'mp', 'server', 'password,refresh_token', NULL, NULL, 43200, 2592001, '{\"enc_flag\":\"1\",\"captcha_flag\":\"1\"}', 'true', '0', ' ', ' ', NULL, NULL, 1);
INSERT INTO `sys_oauth_client_details` VALUES (5, 'pig', NULL, 'pig', 'server', 'password,refresh_token,authorization_code,client_credentials,mobile', 'http://localhost:4040/sso1/login,http://localhost:4041/sso1/login,http://localhost:8080/renren-admin/sys/oauth2-sso,http://localhost:8090/sys/oauth2-sso', NULL, 43200, 2592001, '{\"enc_flag\":\"1\",\"captcha_flag\":\"1\",\"online_quantity\":\"1\"}', 'false', '0', '', 'admin', NULL, '2023-03-08 11:32:41', 1);
INSERT INTO `sys_oauth_client_details` VALUES (5, 'pig', NULL, 'pig', 'server', 'password,refresh_token,authorization_code,mobile', 'http://localhost:4040/sso1/login,http://localhost:4041/sso1/login,http://localhost:8080/renren-admin/sys/oauth2-sso,http://localhost:8090/sys/oauth2-sso', NULL, 43200, 2592001, '{\"enc_flag\":\"1\",\"captcha_flag\":\"1\",\"online_quantity\":\"1\"}', 'false', '0', '', 'admin', NULL, '2023-03-08 11:32:41', 1);
INSERT INTO `sys_oauth_client_details` VALUES (6, 'test', NULL, 'test', 'server', 'password,refresh_token', NULL, NULL, 43200, 2592001, '{ \"enc_flag\":\"1\",\"captcha_flag\":\"0\"}', 'true', '0', ' ', ' ', NULL, NULL, 1);
INSERT INTO `sys_oauth_client_details` VALUES (7, 'social', NULL, 'social', 'server', 'password,refresh_token,mobile', NULL, NULL, 43200, 2592001, '{ \"enc_flag\":\"0\",\"captcha_flag\":\"0\"}', 'true', '0', ' ', ' ', NULL, NULL, 1);
INSERT INTO `sys_oauth_client_details` VALUES (8, 'mini', NULL, 'mini', 'server', 'password,mobile', NULL, NULL, 160000000, 160000000, '{\"captcha_flag\":\"0\",\"enc_flag\":\"0\",\"online_quantity\":\"1\"}', 'true', '0', 'admin', 'admin', '2023-01-29 16:38:06', '2023-01-29 17:21:56', 1);
@@ -880,8 +883,8 @@ CREATE TABLE `sys_public_param` (
`public_value` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '值',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '状态,0禁用,1启用',
`validate_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '校验码',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`public_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '类型,0未知,1系统,2业务',
@@ -910,8 +913,8 @@ INSERT INTO `sys_public_param` VALUES (12, '强制重置密码', 'SITE_FORCE_RES
INSERT INTO `sys_public_param` VALUES (13, '强制退出', 'SITE_FORCE_LOGOUT', '0', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (14, '短信登录开关', 'SITE_SMS_LOGIN_ENABLE', '1', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (15, '社交登录开关', 'SITE_SOCIAL_LOGIN_ENABLE', '1', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (16, '前端注册开关', 'SITE_REGISTER_ENABLE', '1', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (17, '找回/重置密码开关', 'SITE_RESET_PASSWORD', '1', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (16, '前端注册开关', 'SITE_REGISTER_ENABLE', '0', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (17, '找回/重置密码开关', 'SITE_RESET_PASSWORD', '0', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (18, '国际化切换开关', 'SITE_I18N_ENABLE', '1', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (19, '亮暗模式切换开关', 'SITE_DARK_MODE_ENABLE', '1', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (20, '反调试保护', 'SITE_ANTI_DEBUG_ENABLE', '0', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
@@ -940,8 +943,8 @@ CREATE TABLE `sys_role` (
`role_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '角色描述',
`ds_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '2' COMMENT '数据权限类型,0全部,1自定义,2本部门及以下,3本部门,4仅本人',
`ds_scope` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '数据权限范围',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记,0未删除,1已删除',
@@ -1122,6 +1125,7 @@ INSERT INTO `sys_role_menu` VALUES (1, 4011);
INSERT INTO `sys_role_menu` VALUES (1, 4012);
INSERT INTO `sys_role_menu` VALUES (1, 4013);
INSERT INTO `sys_role_menu` VALUES (1, 4014);
INSERT INTO `sys_role_menu` VALUES (1, 4015);
INSERT INTO `sys_role_menu` VALUES (1, 5000);
INSERT INTO `sys_role_menu` VALUES (1, 5001);
INSERT INTO `sys_role_menu` VALUES (1, 5002);
@@ -1195,7 +1199,6 @@ INSERT INTO `sys_role_menu` VALUES (1, 7804);
INSERT INTO `sys_role_menu` VALUES (1, 7805);
INSERT INTO `sys_role_menu` VALUES (1, 9000);
INSERT INTO `sys_role_menu` VALUES (1, 9005);
INSERT INTO `sys_role_menu` VALUES (1, 9006);
INSERT INTO `sys_role_menu` VALUES (1, 9007);
INSERT INTO `sys_role_menu` VALUES (1, 9050);
INSERT INTO `sys_role_menu` VALUES (1, 9051);
@@ -1247,18 +1250,18 @@ CREATE TABLE `sys_role_widget` (
DROP TABLE IF EXISTS `sys_route_conf`;
CREATE TABLE `sys_route_conf` (
`id` bigint(20) NOT NULL COMMENT '主键',
`route_name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`route_id` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`route_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`route_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`predicates` json DEFAULT NULL COMMENT '断言',
`filters` json DEFAULT NULL COMMENT '过滤器',
`uri` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`uri` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`sort_order` int(11) DEFAULT '0' COMMENT '排序',
`metadata` json DEFAULT NULL COMMENT '路由元信息',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`del_flag` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '0',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='路由配置表';
@@ -1295,9 +1298,9 @@ CREATE TABLE `sys_schedule` (
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '内容',
`schedule_time` time DEFAULT NULL COMMENT '时间',
`schedule_date` date DEFAULT NULL COMMENT '日期',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记',
`tenant_id` bigint(20) unsigned DEFAULT NULL COMMENT '租户ID',
@@ -1322,8 +1325,8 @@ CREATE TABLE `sys_social_details` (
`app_secret` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '应用密钥',
`redirect_url` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '回调地址',
`ext` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '拓展字段',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记,0未删除,1已删除',
@@ -1412,21 +1415,14 @@ CREATE TABLE `sys_user` (
`nickname` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '昵称',
`name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '姓名',
`email` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '邮箱地址',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`lock_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '锁定标记,0未锁定,9已锁定',
`password_expire_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '密码是否过期,0未过期,9已过期',
`password_modify_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记,0未删除,1已删除',
`wx_openid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信登录openId',
`mini_openid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '小程序openId',
`qq_openid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'QQ openId',
`gitee_login` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '码云标识',
`osc_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '开源中国标识',
`wx_cp_userid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '企业微信唯一ID',
`wx_ding_userid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '钉钉唯一ID',
`dept_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '主部门ID',
`tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '所属租户ID',
PRIMARY KEY (`user_id`) USING BTREE
@@ -1436,9 +1432,24 @@ CREATE TABLE `sys_user` (
-- Records of sys_user
-- ----------------------------
BEGIN;
INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$c/Ae0pRjJtMZg3BnvVpO.eIK6WYWVbKTzqgdy3afR7w.vd.xi3Mgy', '', '17338122125', '/admin/sys-file/local/2a14ae08150e483c93e12ac8934173e2.png', '管理员666777', '管理员', 'sw@mail.pigxl.vip', ' ', 'admin', '2018-04-20 07:15:18', '2023-04-03 14:00:06', '0', '0', NULL, '0', NULL, 'oBxPy5E-v82xWGsfzZVzkD3wEX64', NULL, 'log4j', NULL, NULL,NULL,1,1);
INSERT INTO `sys_user` (`user_id`, `username`, `password`, `salt`, `phone`, `avatar`, `nickname`, `name`, `email`, `create_by`, `update_by`, `create_time`, `update_time`, `lock_flag`, `password_expire_flag`, `password_modify_time`, `del_flag`, `dept_id`, `tenant_id`) VALUES (1, 'admin', '$2a$10$c/Ae0pRjJtMZg3BnvVpO.eIK6WYWVbKTzqgdy3afR7w.vd.xi3Mgy', '', '17338122125', '/admin/sys-file/local/2a14ae08150e483c93e12ac8934173e2.png', '管理员666777', '管理员', 'sw@mail.pigxl.vip', ' ', 'admin', '2018-04-20 07:15:18', '2023-04-03 14:00:06', '0', '0', NULL, '0', 1, 1);
COMMIT;
-- ----------------------------
-- Table structure for sys_user_social
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_social`;
CREATE TABLE `sys_user_social` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '社交平台类型',
`identifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '唯一凭据',
`create_time` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_social_identifier` (`type`,`identifier`) USING BTREE,
UNIQUE KEY `uk_user_social_type` (`user_id`,`type`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户社交登录身份表';
-- ----------------------------
-- Table structure for sys_user_post
-- ----------------------------
@@ -1528,7 +1539,11 @@ CREATE TABLE `sys_message` (
`update_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '删除时间',
`tenant_id` bigint DEFAULT NULL COMMENT '租户',
PRIMARY KEY (`id`)
`business_type` varchar(32) DEFAULT NULL COMMENT '业务类型',
`business_id` varchar(64) DEFAULT NULL COMMENT '业务记录ID',
`request_key` varchar(100) DEFAULT NULL COMMENT '调用方事件标识',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_message_request` (`tenant_id`,`request_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='站内信息';
-- ----------------------------
@@ -1553,7 +1568,8 @@ CREATE TABLE `sys_message_relation` (
`update_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '删除时间',
`tenant_id` bigint DEFAULT NULL COMMENT '租户',
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `idx_relation_msg_user` (`msg_id`,`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统消息推送记录';
@@ -1696,11 +1712,13 @@ CREATE TABLE `process_node_data` (
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标志',
`create_time` datetime COMMENT '创建时间',
`update_time` datetime COMMENT '更新时间',
`process_definition_id` varchar(64) DEFAULT NULL COMMENT '引擎流程定义ID,发布版本快照',
`flow_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '流程id',
`data` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '表单数据',
`node_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '所属租户id',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_node_definition` (`tenant_id`,`process_definition_id`,`node_id`),
KEY `data_idx_id` (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1195 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='流程节点数据';
@@ -1761,9 +1779,21 @@ CREATE TABLE `process_node_record_assign_user` (
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_node_record_user` (`tenant_id`,`node_record_id`,`user_id`) USING BTREE,
KEY `user_idx_id` (`id`) USING BTREE,
KEY `idx_add_sign_record_id` (`add_sign_record_id`) USING BTREE
KEY `idx_add_sign_record_id` (`add_sign_record_id`) USING BTREE,
KEY `idx_assign_tenant_instance_del` (`tenant_id`,`process_instance_id`,`del_flag`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=597 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='流程节点记录-执行人';
-- Flowable 列表查询索引:act_* 表由引擎初始化,不在本脚本中创建。
-- 首次启动完成后,在业务库手动执行以下 SQL 一次;已有同名索引时不要重复执行。
-- 待办:按审批人筛选、创建时间排序。
-- ALTER TABLE `act_ru_task`
-- ADD INDEX `idx_task_assignee_create` (`ASSIGNEE_`, `CREATE_TIME_`),
-- ALGORITHM=INPLACE, LOCK=NONE;
-- 已办:按审批人及结束时间查询、统计。
-- ALTER TABLE `act_hi_actinst`
-- ADD INDEX `idx_act_assignee_end` (`ASSIGNEE_`, `END_TIME_`),
-- ALGORITHM=INPLACE, LOCK=NONE;
-- ----------------------------
-- Table structure for process_task_add_sign_record
@@ -1791,6 +1821,50 @@ CREATE TABLE `process_task_add_sign_record` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='流程动态加签批次记录';
-- ----------------------------
-- Table structure for flow_draft
-- ----------------------------
DROP TABLE IF EXISTS `flow_draft`;
CREATE TABLE `flow_draft` (
`id` varchar(36) COLLATE utf8mb4_general_ci NOT NULL COMMENT '草稿ID',
`tenant_id` bigint NOT NULL COMMENT '所属租户ID',
`user_id` bigint NOT NULL COMMENT '草稿所属用户ID',
`kind` varchar(16) COLLATE utf8mb4_general_ci NOT NULL COMMENT '草稿类型:DESIGN 流程设计,APPLICATION 申请',
`flow_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '关联流程ID,未建立流程时为空串',
`title` varchar(200) COLLATE utf8mb4_general_ci NOT NULL COMMENT '草稿标题',
`payload` longtext COLLATE utf8mb4_general_ci NOT NULL COMMENT '草稿内容 JSON,最多 1 MB',
`revision` bigint NOT NULL COMMENT '乐观锁版本号',
`updated_at` timestamp(3) NOT NULL COMMENT '最后保存时间',
PRIMARY KEY (`id`),
KEY `idx_draft_owner` (`tenant_id`,`user_id`,`kind`,`updated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='个人流程草稿';
-- ----------------------------
-- Table structure for flow_notice
-- ----------------------------
DROP TABLE IF EXISTS `flow_notice`;
CREATE TABLE `flow_notice` (
`id` varchar(36) COLLATE utf8mb4_general_ci NOT NULL COMMENT '通知事件ID',
`tenant_id` bigint NOT NULL COMMENT '所属租户ID',
`user_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '接收人ID,超时提醒到期前为空串',
`event_key` varchar(180) COLLATE utf8mb4_general_ci NOT NULL COMMENT '事件去重键',
`kind` varchar(16) COLLATE utf8mb4_general_ci NOT NULL COMMENT '通知类型:TODO 待办,URGE 催办,OVERDUE 超时提醒',
`title` varchar(200) COLLATE utf8mb4_general_ci NOT NULL COMMENT '通知标题',
`task_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '引擎任务ID',
`instance_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '引擎流程实例ID',
`created_at` timestamp(3) NOT NULL COMMENT '创建时间',
`status` varchar(16) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'SENT' COMMENT '状态:PENDING 待到期,READY 待投递,SENT 已投递,REMOVING 待撤销,CANCELLED 已取消,DISABLED 已停用',
`scheduled_at` timestamp(3) NULL DEFAULT NULL COMMENT '计划投递或到期时间',
`sent_at` timestamp(3) NULL DEFAULT NULL COMMENT '投递到统一站内信的时间',
`reminder_key` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '超时提醒任务ID,每个任务最多一条',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_notice_event` (`tenant_id`,`user_id`,`event_key`),
UNIQUE KEY `uk_notice_reminder` (`tenant_id`,`reminder_key`),
KEY `idx_notice_owner` (`tenant_id`,`user_id`,`created_at`),
KEY `idx_notice_due` (`status`,`scheduled_at`),
KEY `idx_notice_task` (`tenant_id`,`task_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='流程通知事件';
-- ----------------------------
-- Table structure for process_starter
-- ----------------------------
@@ -1814,31 +1888,31 @@ CREATE TABLE `process_starter` (
DROP TABLE IF EXISTS `bmp_oa_leave`;
CREATE TABLE `bmp_oa_leave` (
`id` bigint NOT NULL COMMENT '请假表单主键',
`username` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '申请人',
`username` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '申请人',
`leave_type` int NOT NULL COMMENT '请假类型',
`leave_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '请假原因',
`leave_reason` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '请假原因',
`start_time` datetime NOT NULL COMMENT '开始时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`leave_day` int NOT NULL COMMENT '请假天数',
`leave_status` int DEFAULT NULL COMMENT '请假结果',
`process_instance_id` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '流程实例的编号',
`create_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
`process_instance_id` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '流程实例的编号',
`create_by` varchar(64) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者',
`create_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
`update_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',
`update_by` varchar(64) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者',
`update_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间',
`del_flag` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '是否删除',
`del_flag` char(1) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='请假表单';
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='请假表单';
-- ----------------------------
-- Table structure for qrtz_blob_triggers
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_blob_triggers`;
CREATE TABLE `qrtz_blob_triggers` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`blob_data` blob,
PRIMARY KEY (`sched_name`,`trigger_name`,`trigger_group`) USING BTREE,
CONSTRAINT `qrtz_blob_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
@@ -1855,8 +1929,8 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_calendars`;
CREATE TABLE `qrtz_calendars` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`calendar_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`calendar_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`calendar` blob NOT NULL,
PRIMARY KEY (`sched_name`,`calendar_name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -1872,11 +1946,11 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_cron_triggers`;
CREATE TABLE `qrtz_cron_triggers` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`cron_expression` varchar(200) CHARACTER SET utf8 NOT NULL,
`time_zone_id` varchar(80) CHARACTER SET utf8 DEFAULT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`cron_expression` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`time_zone_id` varchar(80) CHARACTER SET utf8mb4 DEFAULT NULL,
PRIMARY KEY (`sched_name`,`trigger_name`,`trigger_group`) USING BTREE,
CONSTRAINT `qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -1892,19 +1966,19 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_fired_triggers`;
CREATE TABLE `qrtz_fired_triggers` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`entry_id` varchar(95) CHARACTER SET utf8 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`instance_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`entry_id` varchar(95) CHARACTER SET utf8mb4 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`instance_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`fired_time` bigint NOT NULL,
`sched_time` bigint NOT NULL,
`priority` int NOT NULL,
`state` varchar(16) CHARACTER SET utf8 NOT NULL,
`job_name` varchar(200) CHARACTER SET utf8 DEFAULT NULL,
`job_group` varchar(200) CHARACTER SET utf8 DEFAULT NULL,
`is_nonconcurrent` varchar(1) CHARACTER SET utf8 DEFAULT NULL,
`requests_recovery` varchar(1) CHARACTER SET utf8 DEFAULT NULL,
`state` varchar(16) CHARACTER SET utf8mb4 NOT NULL,
`job_name` varchar(200) CHARACTER SET utf8mb4 DEFAULT NULL,
`job_group` varchar(200) CHARACTER SET utf8mb4 DEFAULT NULL,
`is_nonconcurrent` varchar(1) CHARACTER SET utf8mb4 DEFAULT NULL,
`requests_recovery` varchar(1) CHARACTER SET utf8mb4 DEFAULT NULL,
PRIMARY KEY (`sched_name`,`entry_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -1919,15 +1993,15 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_job_details`;
CREATE TABLE `qrtz_job_details` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`job_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`job_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`description` varchar(250) CHARACTER SET utf8 DEFAULT NULL,
`job_class_name` varchar(250) CHARACTER SET utf8 NOT NULL,
`is_durable` varchar(1) CHARACTER SET utf8 NOT NULL,
`is_nonconcurrent` varchar(1) CHARACTER SET utf8 NOT NULL,
`is_update_data` varchar(1) CHARACTER SET utf8 NOT NULL,
`requests_recovery` varchar(1) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`job_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`job_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`description` varchar(250) CHARACTER SET utf8mb4 DEFAULT NULL,
`job_class_name` varchar(250) CHARACTER SET utf8mb4 NOT NULL,
`is_durable` varchar(1) CHARACTER SET utf8mb4 NOT NULL,
`is_nonconcurrent` varchar(1) CHARACTER SET utf8mb4 NOT NULL,
`is_update_data` varchar(1) CHARACTER SET utf8mb4 NOT NULL,
`requests_recovery` varchar(1) CHARACTER SET utf8mb4 NOT NULL,
`job_data` blob,
PRIMARY KEY (`sched_name`,`job_name`,`job_group`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -1943,8 +2017,8 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_locks`;
CREATE TABLE `qrtz_locks` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`lock_name` varchar(40) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`lock_name` varchar(40) CHARACTER SET utf8mb4 NOT NULL,
PRIMARY KEY (`sched_name`,`lock_name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -1959,8 +2033,8 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_paused_trigger_grps`;
CREATE TABLE `qrtz_paused_trigger_grps` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
PRIMARY KEY (`sched_name`,`trigger_group`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -1975,8 +2049,8 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_scheduler_state`;
CREATE TABLE `qrtz_scheduler_state` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`instance_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`instance_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`last_checkin_time` bigint NOT NULL,
`checkin_interval` bigint NOT NULL,
PRIMARY KEY (`sched_name`,`instance_name`) USING BTREE
@@ -1993,9 +2067,9 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_simple_triggers`;
CREATE TABLE `qrtz_simple_triggers` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`repeat_count` bigint NOT NULL,
`repeat_interval` bigint NOT NULL,
`times_triggered` bigint NOT NULL,
@@ -2014,20 +2088,20 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_simprop_triggers`;
CREATE TABLE `qrtz_simprop_triggers` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`str_prop_1` varchar(512) CHARACTER SET utf8 DEFAULT NULL,
`str_prop_2` varchar(512) CHARACTER SET utf8 DEFAULT NULL,
`str_prop_3` varchar(512) CHARACTER SET utf8 DEFAULT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`str_prop_1` varchar(512) CHARACTER SET utf8mb4 DEFAULT NULL,
`str_prop_2` varchar(512) CHARACTER SET utf8mb4 DEFAULT NULL,
`str_prop_3` varchar(512) CHARACTER SET utf8mb4 DEFAULT NULL,
`int_prop_1` int DEFAULT NULL,
`int_prop_2` int DEFAULT NULL,
`long_prop_1` bigint DEFAULT NULL,
`long_prop_2` bigint DEFAULT NULL,
`dec_prop_1` decimal(13,4) DEFAULT NULL,
`dec_prop_2` decimal(13,4) DEFAULT NULL,
`bool_prop_1` varchar(1) CHARACTER SET utf8 DEFAULT NULL,
`bool_prop_2` varchar(1) CHARACTER SET utf8 DEFAULT NULL,
`bool_prop_1` varchar(1) CHARACTER SET utf8mb4 DEFAULT NULL,
`bool_prop_2` varchar(1) CHARACTER SET utf8mb4 DEFAULT NULL,
PRIMARY KEY (`sched_name`,`trigger_name`,`trigger_group`) USING BTREE,
CONSTRAINT `QRTZ_SIMPROP_TRIGGERS_IBFK_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -2043,20 +2117,20 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_triggers`;
CREATE TABLE `qrtz_triggers` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`job_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`job_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`description` varchar(250) CHARACTER SET utf8 DEFAULT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`job_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`job_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`description` varchar(250) CHARACTER SET utf8mb4 DEFAULT NULL,
`next_fire_time` bigint DEFAULT NULL,
`prev_fire_time` bigint DEFAULT NULL,
`priority` int DEFAULT NULL,
`trigger_state` varchar(16) CHARACTER SET utf8 NOT NULL,
`trigger_type` varchar(8) CHARACTER SET utf8 NOT NULL,
`trigger_state` varchar(16) CHARACTER SET utf8mb4 NOT NULL,
`trigger_type` varchar(8) CHARACTER SET utf8mb4 NOT NULL,
`start_time` bigint NOT NULL,
`end_time` bigint DEFAULT NULL,
`calendar_name` varchar(200) CHARACTER SET utf8 DEFAULT NULL,
`calendar_name` varchar(200) CHARACTER SET utf8mb4 DEFAULT NULL,
`misfire_instr` smallint DEFAULT NULL,
`job_data` blob,
PRIMARY KEY (`sched_name`,`trigger_name`,`trigger_group`) USING BTREE,
@@ -2114,22 +2188,22 @@ DROP TABLE IF EXISTS `sys_job_log`;
CREATE TABLE `sys_job_log` (
`job_log_id` bigint NOT NULL COMMENT '任务日志ID',
`job_id` bigint NOT NULL COMMENT '任务id',
`job_name` varchar(64) CHARACTER SET utf8 DEFAULT NULL COMMENT '任务名称',
`job_group` varchar(64) CHARACTER SET utf8 DEFAULT NULL COMMENT '任务组名',
`job_order` char(1) CHARACTER SET utf8 DEFAULT NULL COMMENT '组内执行顺利,值越大执行优先级越高,最大值9,最小值1',
`job_type` char(1) CHARACTER SET utf8 NOT NULL DEFAULT '1' COMMENT '1、java类;2、spring bean名称;3、rest调用;4、jar调用;9其他',
`execute_path` varchar(500) CHARACTER SET utf8 DEFAULT NULL COMMENT 'job_type=3时,rest调用地址,仅支持post协议;job_type=4时,jar路径;其它值为空',
`class_name` varchar(500) CHARACTER SET utf8 DEFAULT NULL COMMENT 'job_type=1时,类完整路径;job_type=2时,spring bean名称;其它值为空',
`method_name` varchar(500) CHARACTER SET utf8 DEFAULT NULL COMMENT '任务方法',
`method_params_value` varchar(2000) CHARACTER SET utf8 DEFAULT NULL COMMENT '参数值',
`cron_expression` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT 'cron执行表达式',
`job_message` varchar(500) CHARACTER SET utf8 DEFAULT NULL COMMENT '日志信息',
`job_log_status` char(1) CHARACTER SET utf8 DEFAULT '0' COMMENT '执行状态(0正常 1失败)',
`execute_time` varchar(30) CHARACTER SET utf8 DEFAULT NULL COMMENT '执行时间',
`exception_info` varchar(2000) CHARACTER SET utf8 DEFAULT '' COMMENT '异常信息',
`job_name` varchar(64) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '任务名称',
`job_group` varchar(64) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '任务组名',
`job_order` char(1) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '组内执行顺利,值越大执行优先级越高,最大值9,最小值1',
`job_type` char(1) CHARACTER SET utf8mb4 NOT NULL DEFAULT '1' COMMENT '1、java类;2、spring bean名称;3、rest调用;4、jar调用;9其他',
`execute_path` varchar(500) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'job_type=3时,rest调用地址,仅支持post协议;job_type=4时,jar路径;其它值为空',
`class_name` varchar(500) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'job_type=1时,类完整路径;job_type=2时,spring bean名称;其它值为空',
`method_name` varchar(500) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '任务方法',
`method_params_value` varchar(2000) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '参数值',
`cron_expression` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'cron执行表达式',
`job_message` varchar(500) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '日志信息',
`job_log_status` char(1) CHARACTER SET utf8mb4 DEFAULT '0' COMMENT '执行状态(0正常 1失败)',
`execute_time` varchar(30) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '执行时间',
`exception_info` varchar(2000) CHARACTER SET utf8mb4 DEFAULT '' COMMENT '异常信息',
`scheduled_fire_time` datetime DEFAULT NULL COMMENT '计划触发时间',
`fire_instance_id` varchar(128) CHARACTER SET utf8 DEFAULT NULL COMMENT 'Quartz触发实例ID',
`dedup_status` char(1) CHARACTER SET utf8 DEFAULT '0' COMMENT '去重状态(0正常执行 1同一触发点重复 2任务运行中跳过)',
`fire_instance_id` varchar(128) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'Quartz触发实例ID',
`dedup_status` char(1) CHARACTER SET utf8mb4 DEFAULT '0' COMMENT '去重状态(0正常执行 1同一触发点重复 2任务运行中跳过)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`tenant_id` bigint NOT NULL DEFAULT '1' COMMENT '租户id',
PRIMARY KEY (`job_log_id`) USING BTREE
@@ -2249,8 +2323,8 @@ CREATE TABLE `wx_auto_reply` (
`rep_thumb_url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '缩略图url',
`content` json DEFAULT NULL COMMENT '图文消息的内容',
`app_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '公众号ID',
`remark` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注',
`del_flag` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '0' COMMENT '逻辑删除标记(0:显示;1:隐藏)',
`remark` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '逻辑删除标记(0:显示;1:隐藏)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`tenant_id` bigint(20) DEFAULT NULL COMMENT '租户ID',
@@ -2295,7 +2369,7 @@ CREATE TABLE `wx_msg` (
`id` bigint(20) NOT NULL COMMENT '主键',
`app_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '公众号名称',
`app_logo` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '公众号logo',
`wx_user_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '微信用户ID',
`wx_user_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '微信用户ID',
`nick_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信用户昵称',
`headimg_url` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信用户头像',
`type` char(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '消息分类(1、用户发给公众号;2、公众号发给用户;)',
@@ -2313,11 +2387,11 @@ CREATE TABLE `wx_msg` (
`rep_location_x` double DEFAULT NULL COMMENT '地理位置维度',
`rep_location_y` double DEFAULT NULL COMMENT '地理位置经度',
`rep_scale` double DEFAULT NULL COMMENT '地图缩放大小',
`read_flag` char(2) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '1' COMMENT '已读标记(1:是;0:否)',
`read_flag` char(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1' COMMENT '已读标记(1:是;0:否)',
`app_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '公众号ID',
`open_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信唯一标识',
`remark` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注',
`del_flag` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '0' COMMENT '逻辑删除标记(0:显示;1:隐藏)',
`remark` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '逻辑删除标记(0:显示;1:隐藏)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`tenant_id` bigint(20) DEFAULT NULL COMMENT '租户ID',
@@ -2607,29 +2681,6 @@ INSERT INTO `gen_field_type` (`id`, `column_type`, `attr_type`, `package_name`,
INSERT INTO `gen_field_type` (`id`, `column_type`, `attr_type`, `package_name`, `default_form_type`, `default_query_form_type`, `create_time`, `create_by`, `update_time`, `update_by`, `del_flag`) VALUES (49,'bytea','String',NULL,'textarea','text','2026-04-07 20:47:09',NULL,'2026-04-07 20:47:09',NULL,'0');
COMMIT;
-- ----------------------------
-- Table structure for gen_form_conf
-- ----------------------------
DROP TABLE IF EXISTS `gen_form_conf`;
CREATE TABLE `gen_form_conf` (
`id` bigint NOT NULL COMMENT 'ID',
`ds_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '数据库名称',
`table_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '表名称',
`form_info` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '表单信息',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0',
`tenant_id` bigint DEFAULT NULL COMMENT '所属租户',
PRIMARY KEY (`id`) USING BTREE,
KEY `table_name` (`table_name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='表单配置';
-- ----------------------------
-- Records of gen_form_conf
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for gen_group
-- ----------------------------
@@ -2639,8 +2690,8 @@ CREATE TABLE `gen_group` (
`group_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '分组名称',
`group_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '分组描述',
`tenant_id` bigint NOT NULL COMMENT '租户ID',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT NULL COMMENT '创建人',
`update_time` datetime DEFAULT NULL COMMENT '修改人',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记',
@@ -2651,10 +2702,10 @@ CREATE TABLE `gen_group` (
-- Records of gen_group
-- ----------------------------
BEGIN;
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (1,'单表增删改查V2026001',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (2,'主子表增删改查V2026001',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (3,'树形表格增删改查V2026001',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (4,'左树右表增删改查V2026001',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (1,'单表增删改查V202611',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (2,'主子表增删改查V202611',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (3,'树形表格增删改查V202611',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (4,'左树右表增删改查V202611',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
COMMIT;
-- ----------------------------
@@ -2750,8 +2801,8 @@ CREATE TABLE `gen_template` (
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT '删除标记',
`tenant_id` bigint NOT NULL COMMENT '租户ID',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='模板';
@@ -2759,51 +2810,51 @@ CREATE TABLE `gen_template` (
-- Records of gen_template
-- ----------------------------
BEGIN;
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (1,'ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #elsLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (2,'ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${entityClassName};\n\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (3,'ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (4,'实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (5,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (6,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (7,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (8,'api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n\n#if($ChildClassName)\n// ========== 子表相关接口 ==========\n\n/**\n * 删除子表数据\n * @param ids - 要删除的子表数据ID数组\n * @returns Promise<操作结果>\n */\nexport function delChildObj(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/child\',\n method: \'delete\',\n data: ids\n })\n}\n#end\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (9,'表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','表格V2026001','<template>\n <div class=\"layout-padding\">\n <div class=\"layout-padding-auto layout-padding-view\">\n#if($queryList)\n <!-- 查询表单区域 -->\n <el-row v-show=\"showSearch\">\n <el-form :model=\"state.queryForm\" ref=\"queryRef\" :inline=\"true\" @keyup.enter=\"getDataList\">\n#foreach($field in $queryList)\n#if($field.queryFormType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"state.queryForm.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option \n :label=\"item.label\" \n :value=\"item.value\" \n v-for=\"(item, index) in ${field.fieldDict}\" \n :key=\"index\"\n />\n#else\n <el-option label=\"请选择\" value=\"0\" />\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker\n type=\"date\"\n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"\n v-model=\"state.queryForm.${field.attrName}\"\n :value-format=\"dateStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}Range\">\n <el-date-picker\n type=\"daterange\"\n range-separator=\"至\"\n start-placeholder=\"开始日期\"\n end-placeholder=\"结束日期\"\n v-model=\"state.queryForm.${field.attrName}Range\"\n :value-format=\"dateStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetime\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker\n type=\"datetime\"\n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"\n v-model=\"state.queryForm.${field.attrName}\"\n :value-format=\"dateTimeStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetimerange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}Range\">\n <el-date-picker\n type=\"datetimerange\"\n range-separator=\"至\"\n start-placeholder=\"开始时间\"\n end-placeholder=\"结束时间\"\n v-model=\"state.queryForm.${field.attrName}Range\"\n :value-format=\"dateTimeStr\"\n />\n </el-form-item>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"state.queryForm.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio \n :label=\"item.value\" \n v-for=\"(item, index) in ${field.fieldDict}\" \n border \n :key=\"index\"\n >\n {{ item.label }}\n </el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>\n ${field.fieldComment}\n </el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n#else\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input \n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" \n v-model=\"state.queryForm.${field.attrName}\" \n />\n </el-form-item>\n#end\n#end\n <el-form-item>\n <el-button icon=\"search\" type=\"primary\" @click=\"getDataList\">\n 查询\n </el-button>\n <el-button icon=\"Refresh\" @click=\"resetQuery\">重置</el-button>\n </el-form-item>\n </el-form>\n </el-row>\n#end\n\n <!-- 操作按钮区域 -->\n <el-row>\n <div class=\"mb8\" style=\"width: 100Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (10,'表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','表单V2026001','<template>\n <el-dialog :title=\"form.${pk.attrName} ? \'编辑\' : \'新增\'\" v-model=\"visible\"\n :close-on-click-modal=\"false\" draggable>\n <el-form ref=\"dataFormRef\" :model=\"form\" :rules=\"dataRules\" label-width=\"90px\" v-loading=\"loading\">\n <el-row :gutter=\"24\">\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName})\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n#if($field.formType == \'text\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input type=\"textarea\" v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"form.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option :value=\"item.value\" :label=\"item.label\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\"></el-option>\n#else\n <el-option label=\"请选择\" value=\"0\"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" border :key=\"index\">{{ item.label }}</el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'checkbox\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-checkbox-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-checkbox :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\">{{ item.label }}</el-checkbox>\n#else\n <el-checkbox label=\"启用\" name=\"type\"></el-checkbox>\n <el-checkbox label=\"禁用\" name=\"type\"></el-checkbox>\n#end\n </el-checkbox-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"date\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" v-model=\"form.${field.attrName}\" :value-format=\"dateStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'datetime\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"datetime\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" v-model=\"form.${field.attrName}\" :value-format=\"dateTimeStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'daterange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"daterange\" range-separator=\"至\" start-placeholder=\"开始日期\" end-placeholder=\"结束日期\" v-model=\"form.${field.attrName}\" :value-format=\"dateStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'datetimerange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (11,'主子ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','主子ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport com.github.yulichang.extension.mapping.base.MPJDeepService;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\n\npublic interface ${ClassName}Service extends MPJDeepService<${ClassName}Entity> {\n Boolean saveDeep(${ClassName}Entity ${className});\n\n Boolean updateDeep(${ClassName}Entity ${className});\n\n Boolean removeDeep(Long[] ids);\n\n Boolean removeChild(Long[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (12,'主子ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','主子ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport org.springframework.transaction.annotation.Transactional;\nimport lombok.RequiredArgsConstructor;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveDeep(${ClassName}Entity ${className}) {\n baseMapper.insert(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert( ${childClassName});\n }\n\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateDeep(${ClassName}Entity ${className}) {\n baseMapper.updateById(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n#set($getChildPkName=$str.getProperty(${pk.attrName}))\n if (Objects.isNull(${childClassName}.$getChildPkName())) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert(${childClassName});\n } else {\n ${childClassName}Mapper.updateById(${childClassName});\n }\n }\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeDeep(Long[] ids) {\n baseMapper.deleteBatchIds(CollUtil.toList(ids));\n ${childClassName}Mapper.delete(Wrappers.<${ChildClassName}Entity>lambdaQuery().in(${ChildClassName}Entity::$str.getProperty($childField), ids));\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeChild(Long[] ids) {\n ${childClassName}Mapper.deleteBatchIds(CollUtil.toList(ids));\n return Boolean.TRUE;\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (13,'主实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','主实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\nimport org.apache.fesod.sheet.annotation.ExcelIgnore;\nimport com.github.yulichang.annotation.EntityMapping;\nimport java.util.List;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n @TableField(typeHandler = JacksonTypeHandler.class)\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n @ExcelIgnore\n @TableField(exist = false)\n @EntityMapping(thisField = \"$mainField\", joinField = \"$childField\")\n private List<${ChildClassName}Entity> ${childClassName}List;\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (14,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (15,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (16,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (17,'api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n\n#if($ChildClassName)\n// ========== 子表相关接口 ==========\n\n/**\n * 删除子表数据\n * @param ids - 要删除的子表数据ID数组\n * @returns Promise<操作结果>\n */\nexport function delChildObj(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/child\',\n method: \'delete\',\n data: ids\n })\n}\n#end\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (18,'子实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ChildClassName}Entity.java','子实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(\"${childTableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (19,'主子ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','主子ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (20,'主子表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','主子表单V2026001','<template>\n <el-drawer :title=\"form.${pk.attrName} ? (detail ? \'详情\' : \'编辑\') : \'添加\'\" v-model=\"visible\" size=\"50%\">\n <el-form ref=\"dataFormRef\" :model=\"form\" :rules=\"dataRules\" :disabled=\"detail\" v-loading=\"loading\">\n <el-row :gutter=\"24\">\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName})\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n#if($field.formType == \'text\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-input v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-input type=\"textarea\" v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"form.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\">\n#if($field.fieldDict)\n <el-option :value=\"item.value\" :label=\"item.label\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\"></el-option>\n#else\n <el-option label=\"请选择\" value=\"0\"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" border :key=\"index\">{{ item.label }}</el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'checkbox\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-checkbox-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-checkbox :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\">{{ item.label }}</el-checkbox>\n#else\n <el-checkbox label=\"启用\" name=\"type\"></el-checkbox>\n <el-checkbox label=\"禁用\" name=\"type\"></el-checkbox>\n#end\n </el-checkbox-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"date\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" v-model=\"form.${field.attrName}\" :value-format=\"dateStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'datetime\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"datetime\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" v-model=\"form.${field.attrName}\" :value-format=\"dateTimeStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'daterange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"daterange\" range-separator=\"至\" start-placeholder=\"开始日期\" end-placeholder=\"结束日期\" v-model=\"form.${field.attrName}\" :value-format=\"dateStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'datetimerange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldNaLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (21,'主子表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','主子表格V2026001','<template>\n <div class=\"layout-padding\">\n <div class=\"layout-padding-auto layout-padding-view\">\n#if($queryList)\n <el-row v-show=\"showSearch\">\n <el-form :model=\"state.queryForm\" ref=\"queryRef\" :inline=\"true\" @keyup.enter=\"getDataList\">\n#foreach($field in $queryList)\n#if($field.queryFormType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"state.queryForm.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option \n :label=\"item.label\" \n :value=\"item.value\" \n v-for=\"(item, index) in ${field.fieldDict}\" \n :key=\"index\">\n </el-option>\n#else\n <el-option label=\"请选择\">0</el-option>\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker\n type=\"date\"\n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"\n :value-format=\"dateStr\"\n v-model=\"state.queryForm.${field.attrName}\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}Range\">\n <el-date-picker\n type=\"daterange\"\n range-separator=\"至\"\n start-placeholder=\"开始日期\"\n end-placeholder=\"结束日期\"\n v-model=\"state.queryForm.${field.attrName}Range\"\n :value-format=\"dateStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetime\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker\n type=\"datetime\"\n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"\n :value-format=\"dateTimeStr\"\n v-model=\"state.queryForm.${field.attrName}\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetimerange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}Range\">\n <el-date-picker\n type=\"datetimerange\"\n range-separator=\"至\"\n start-placeholder=\"开始时间\"\n end-placeholder=\"结束时间\"\n v-model=\"state.queryForm.${field.attrName}Range\"\n :value-format=\"dateTimeStr\"\n />\n </el-form-item>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"state.queryForm.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio \n :label=\"item.value\" \n v-for=\"(item, index) in ${field.fieldDict}\" \n border \n :key=\"index\">\n {{ item.label }}\n </el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n#else\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input \n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" \n v-model=\"state.queryForm.${field.attrName}\"\n style=\"max-width: 180px\" \n />\n </el-form-item>\n#end\n#end\n <el-form-item>\n <el-button icon=\"search\" type=\"primary\" @click=\"getDataList\">\n 查询\n </el-button>\n <el-button icon=\"Refresh\" @click=\"resetQuery\">重置</el-button>\n </el-form-item>\n </el-form>\n </el-row>\n#end\n <el-row>\n <div class=\"mb8\" style=\"width: 100%\">\n <el-button \n icon=\"folder-adLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (22,'子MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV2026001','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (23,'树形ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','树形ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 获取树形列表\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"获取树形列表\" , description = \"获取树形列表\" )\n @GetMapping(\"/tree\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Tree(@ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.buildTree(wrapper));\n }\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (24,'树形ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','树形ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${entityClassName};\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List<Tree<${pk.attrType}>> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper);\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n boolean removeBatchByIds(List<${pk.attrType}> ids);\n} ','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (25,'树形ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','树形ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List<Tree<${pk.attrType}>> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper) {\n // 查询所有数据\n List<${ClassName}Entity> allList = list(wrapper);\n \n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n // 转换为TreeNode\n List<TreeNode<${pk.attrType}>> collect = allList.stream().map(getNodeFunction()).toList();\n\n // 使用TreeUtil构建树形结构,根节点ID为0\n#if($pk.attrType == \'Long\')\n return TreeUtil.build(collect, 0L);\n#else\n return TreeUtil.build(collect, 0);\n#end\n }\n\n /**\n * 获取TreeNode转换函数\n * @return TreeNode转换函数\n */\n @NotNull\n private Function<${ClassName}Entity, TreeNode<${pk.attrType}>> getNodeFunction() {\n return entity -> {\n TreeNode<${pk.attrType}> node = new TreeNode<>();\n node.setId(entity.$str.getProperty($pk.attrName)());\n node.setName(entity.$str.getProperty($nameField)());\n node.setParentId(entity.$str.getProperty($parentField)() != null ? entity.$str.getProperty($parentField)() : 0L);\n\n // 扩展属性\n Map<String, Object> extra = new HashMap<>();\n#foreach($field in $fieldList)\n extra.put(${ClassName}Entity.Fields.${field.attrName}, entity.$str.getProperty($field.attrName)());\n#end\n node.setExtra(extra);\n return node;\n };\n }\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public boolean removeBatchByIds(List<${pk.attrType}> ids) {\n if (CollUtil.isEmpty(ids)) {\n return true;\n }\n\n // 获取所有要删除的ID(包括子节点)\n List<${pk.attrType}> allDeleteIds = new ArrayList<>(ids);\n \n for (${pk.attrType} id : ids) {\n List<${pk.attrType}> childIds = getChildIdsRecursive(id);\n allDeleteIds.addAll(childIds);\n }\n\n // 去重\n allDeleteIds = allDeleteIds.stream().distinct().collect(Collectors.toList());\n\n // 批量删除\n return super.removeBatchByIds(allDeleteIds);\n }\n\n /**\n * 递归获取所有子节点ID\n * @param parentId 父节点ID\n * @return 子节点ID列表\n */\n private List<${pk.attrType}> getChildIdsRecursive(${pk.attrType} parentId) {\n List<${pk.attrType}> childIds = new ArrayList<>();\n \n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n wrapper.eq(${ClassName}Entity::$str.getProperty($parentField), parentId);\n wrapper.select(${ClassName}Entity::$str.getProperty($pk.attrName));\n \n List<${ClassName}Entity> children = list(wrapper);\n \n for (${ClassName}Entity child : children) {\n ${pk.attrType} childId = child.$str.getProperty($pk.attrName)();\n childIds.add(childId);\n // 递归获取子节点的子节点\n childIds.addAll(getChildIdsRecursive(childId));\n }\n \n return childIds;\n }\n} ','2026-04-08 17:38Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (26,'树形实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','树形实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.experimental.FieldNameConstants;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@FieldNameConstants\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (27,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (28,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (29,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (30,'树形api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','树形api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 树形表格CRUD接口 ==========\n\n/**\n * 获取树形列表数据\n * @param query - 查询参数对象\n * @returns Promise<树形数据>\n */\nexport function fetchTreeList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据(递归删除子节点)\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (31,'树形表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','树形表格V2026001','<template>\n <div class=\"layout-padding\">\n <div class=\"layout-padding-auto layout-padding-view\">\n#if($queryList)\n <!-- 查询表单区域 -->\n <el-row v-show=\"showSearch\">\n <el-form :model=\"state.queryForm\" ref=\"queryRef\" :inline=\"true\" @keyup.enter=\"getDataList\">\n#foreach($field in $queryList)\n#if($field.queryFormType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"state.queryForm.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option \n :label=\"item.label\" \n :value=\"item.value\" \n v-for=\"(item, index) in ${field.fieldDict}\" \n :key=\"index\"\n />\n#else\n <el-option label=\"请选择\" value=\"0\" />\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker \n type=\"date\" \n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" \n v-model=\"state.queryForm.${field.attrName}\"\n :value-format=\"dateStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetime\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker\n type=\"datetime\"\n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"\n v-model=\"state.queryForm.${field.attrName}\"\n :value-format=\"dateTimeStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}Range\">\n <el-date-picker\n type=\"daterange\"\n range-separator=\"至\"\n start-placeholder=\"开始日期\"\n end-placeholder=\"结束日期\"\n v-model=\"state.queryForm.${field.attrName}Range\"\n :value-format=\"dateStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetimerange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}Range\">\n <el-date-picker\n type=\"datetimerange\"\n range-separator=\"至\"\n start-placeholder=\"开始时间\"\n end-placeholder=\"结束时间\"\n v-model=\"state.queryForm.${field.attrName}Range\"\n :value-format=\"dateTimeStr\"\n />\n </el-form-item>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"state.queryForm.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio \n :label=\"item.value\" \n v-for=\"(item, index) in ${field.fieldDict}\" \n border \n :key=\"index\"\n >\n {{ item.label }}\n </el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>\n ${field.fieldComment}\n </el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n#else\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input \n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" \n v-model=\"state.queryForm.${field.attrName}\" \n />\n </el-form-item>\n#end\n#end\n <el-form-item>\n <el-button icon=\"search\" type=\"primary\" @click=\"getDataList\">\n 查询\n </el-button>\n <el-button icon=\"Refresh\" @click=\"resetQuery\">重置</el-button>\n </el-form-item>\n </el-form>\n </el-row>\n#end\n\n <!-- 操作按钮区域 -->\n <el-row>\n <div class=\"mb8\" stLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (32,'树形表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','树形表单V2026001','<template>\n <el-dialog :title=\"form.${pk.attrName} ? \'编辑\' : \'新增\'\" v-model=\"visible\"\n :close-on-click-modal=\"false\" draggable>\n <el-form ref=\"dataFormRef\" :model=\"form\" :rules=\"dataRules\" formDialogRef label-width=\"90px\" v-loading=\"loading\">\n <el-row :gutter=\"24\">\n <!-- 父级节点选择 -->\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n <el-form-item label=\"父级节点\" prop=\"${parentField}\">\n <el-tree-select\n v-model=\"form.${parentField}\"\n :data=\"parentNodes\"\n :props=\"treeSelectProps\"\n check-strictly\n :render-after-expand=\"false\"\n placeholder=\"请选择父级节点\"\n style=\"width: 100%\"\n clearable\n filterable\n />\n </el-form-item>\n </el-col>\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName} && $field.attrName != ${parentField})\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n#if($field.formType == \'text\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input type=\"textarea\" v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"form.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option :value=\"item.value\" :label=\"item.label\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\"></el-option>\n#else\n <el-option label=\"请选择\" value=\"0\"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" border :key=\"index\">{{ item.label }}</el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'checkbox\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-checkbox-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-checkbox :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\">{{ item.label }}</el-checkbox>\n#else\n <el-checkbox label=\"启用\" name=\"type\"></el-checkbox>\n <el-checkbox label=\"禁用\" name=\"type\"></el-checkbox>\n#end\n </el-checkbox-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"date\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" v-model=\"form.${field.attrName}\" :value-format=\"dateStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'datetime\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"datetime\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${fieLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (33,'ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','ControllerV2026001','package ${package}.${moduleName}.controller;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($childFieldGetter = $str.getProperty($childField))\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n#if($childField)\n if (Objects.nonNull(${className}.$childFieldGetter())) {\n wrapper.eq(${ClassName}Entity::$str.getProperty($childField), ${className}.$childFieldGetter());\n }\n#end\n return R.ok(${className}Service.pLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (34,'ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List<Tree<${childPkField.attrType}>> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper);\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName});\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n Boolean saveTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n Boolean updateTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n Boolean removeTreeByIds(${childPkField.attrType}[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (35,'ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#set($childPkGetter = $str.getProperty($childPkField.attrName))\n#break\n#end\n#end\n#set($parentGetter = $str.getProperty($parentField))\n#set($childFieldGetter = $str.getProperty($childField))\n#if($childPkField.attrType == \'Long\')\n#set($treeRootId = \'0L\')\n#elseif($childPkField.attrType == \'String\')\n#set($treeRootId = \'\"0\"\')\n#else\n#set($treeRootId = \'0\')\n#end\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List<Tree<${childPkField.attrType}>> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper) {\n List<${ChildClassName}Entity> allList = ${childClassName}Mapper.selectList(wrapper);\n\n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n List<TreeNode<${childPkField.attrType}>> collect = allList.stream().map(getNodeFunction()).toList();\n return TreeUtil.build(collect, ${treeRootId});\n }\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n @Override\n public List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName}) {\n return ${childClassName}Mapper.selectList(Wrappers.query(${childClassName}));\n }\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(null, ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.insert(${childClassName}) > 0;\n }\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(${childClassName}.$str.getProperty($childPkField.attrName)(), ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.updateById(${childClassName}) > 0;\n }\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeTreeByIds(${childPkField.attrType}[] ids) {\n if (ArrayUtil.isEmpty(ids)) {\n return Boolean.TRUE;\n }\n\n for (${childPkField.attrType} id : ids) {\n if (hasChildNodes(id)) {\n throw new RuntimeException(\"当前节点存在子节点,不能删除\");\n }\n if (hasMainRecords(id)) {\n throw new RuntimeException(\"当前节点已关联主表数据,不能删除\");\n }\n }\n\n return ${childClassName}Mapper.deleteBatchLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (36,'实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (37,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (38,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (39,'子实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ChildClassName}Entity.java','子实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(\"${childTableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (40,'子MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV2026001','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (41,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (42,'api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 树形CRUD接口 ==========\n\n/**\n * 获取树形列表数据\n * @param query - 查询参数对象\n * @returns Promise<树形数据>\n */\nexport function fetchTreeList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 获取树节点详情\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<树节点详情>\n */\nexport function getTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 新增树节点\n * @param obj - 要新增的树节点对象\n * @returns Promise<操作结果>\n */\nexport function addTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 更新树节点\n * @param obj - 要更新的树节点对象\n * @returns Promise<操作结果>\n */\nexport function putTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'put\',\n data: obj\n })\n}\n\n/**\n * 删除树节点\n * @param ids - 要删除的树节点ID数组\n * @returns Promise<操作结果>\n */\nexport function delTreeObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'delete\',\n data: ids\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n *\n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (43,'表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','表格V2026001','#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n<template>\n <div class=\"layout-padding\">\n <splitpanes>\n <pane size=\"15\">\n <div class=\"layout-padding-auto layout-padding-view\">\n <el-scrollbar>\n <div class=\"mb8\" style=\"display: flex; align-items: center; justify-content: space-between;\">\n <span style=\"font-weight: 500;\">左侧节点</span>\n <div>\n <el-tooltip :content=\"currentTreeId ? \'新增子节点\' : \'新增根节点\'\" placement=\"top\">\n <el-button link type=\"primary\" icon=\"FolderAdd\" @click=\"handleAddTree\" />\n </el-tooltip>\n </div>\n </div>\n <el-tree\n ref=\"treeRef\"\n :data=\"treeData\"\n :props=\"treeProps\"\n node-key=\"${childPkField.attrName}\"\n highlight-current\n default-expand-all\n :expand-on-click-node=\"false\"\n @node-click=\"handleTreeNodeClick\"\n >\n <template #default=\"{ node, data }\">\n <span style=\"flex: 1; display: flex; align-items: center; justify-content: space-between;\">\n <span>{{ node.label }}</span>\n <span v-if=\"currentTreeId === data.${childPkField.attrName}\" style=\"margin-left: 8px;\">\n <el-tooltip content=\"编辑\" placement=\"top\">\n <el-button link type=\"primary\" icon=\"Edit\" size=\"small\" @click.stop=\"handleEditTree\" />\n </el-tooltip>\n <el-tooltip content=\"删除\" placement=\"top\">\n <el-button link type=\"danger\" icon=\"Delete\" size=\"small\" @click.stop=\"handleDeleteTree\" />\n </el-tooltip>\n </span>\n </span>\n </template>\n </el-tree>\n </el-scrollbar>\n </div>\n </pane>\n <pane class=\"ml8\">\n <div class=\"layout-padding-auto layout-padding-view\">\n#if($queryList)\n <el-row v-show=\"showSearch\">\n <el-form :model=\"state.queryForm\" ref=\"queryRef\" :inline=\"true\" @keyup.enter=\"getDataList\">\n#foreach($field in $queryList)\n#if($field.attrName != ${childField})\n#if($field.queryFormType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"state.queryForm.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option \n :label=\"item.label\" \n :value=\"item.value\" \n v-for=\"(item, index) in ${field.fieldDict}\" \n :key=\"index\"\n />\n#else\n <el-option label=\"请选择\" value=\"0\" />\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker\n type=\"date\"\n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"\n v-model=\"state.queryForm.${field.attrName}\"\n :value-format=\"dateStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}Range\">\n <el-date-picker\n type=\"daterange\"\n range-separator=\"至\"\n start-placeholder=\"开始日期\"\n end-placeholder=\"结束日期\"\n v-model=\"state.queryForm.${field.attrName}Range\"\n :value-format=\"dateStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetime\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker\n type=\"datetime\"\n placeholder=\"请输入#if(${field.fieldComment})${field.fieLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (44,'树表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/tree-form.vue','树表单V2026001','#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($fieldDict=[])\n#foreach($field in $childFieldList)\n#if($field.fieldDict)\n#set($void=$fieldDict.add($field.fieldDict))\n#end\n#end\n<template>\n <el-dialog :title=\"form.${childPkField.attrName} ? \'编辑\' : \'新增\'\" v-model=\"visible\"\n :close-on-click-modal=\"false\" draggable>\n <el-form ref=\"dataFormRef\" :model=\"form\" :rules=\"dataRules\" label-width=\"90px\" v-loading=\"loading\">\n <el-row :gutter=\"24\">\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n <el-form-item label=\"父级节点\" prop=\"${parentField}\">\n <el-tree-select\n v-model=\"form.${parentField}\"\n :data=\"parentNodes\"\n :props=\"treeSelectProps\"\n check-strictly\n :render-after-expand=\"false\"\n placeholder=\"请选择父级节点\"\n style=\"width: 100%\"\n clearable\n filterable\n />\n </el-form-item>\n </el-col>\n#foreach($field in $childFieldList)\n#if($field.attrName != ${childPkField.attrName} && $field.attrName != ${parentField})\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n#if($field.formType == \'text\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input type=\"textarea\" v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"form.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option :value=\"item.value\" :label=\"item.label\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\"></el-option>\n#else\n <el-option label=\"请选择\" value=\"0\"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" border :key=\"index\">{{ item.label }}</el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'checkbox\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-checkbox-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-checkbox :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\">{{ item.label }}</el-checkbox>\n#else\n <el-checkbox label=\"启用\" name=\"type\"></el-checkbox>\n <el-checkbox label=\"禁用\" name=\"type\"></el-checkbox>\n#end\n </el-checkbox-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"date\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" v-model=\"form.${field.attrName}\" :value-format=\"dateStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'datetime\')\n <el-form-item label=\Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (45,'主表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','主表单V2026001','#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($relationFieldLabel = $childField)\n#foreach($field in $fieldList)\n#if($field.attrName == $childField)\n#if($field.fieldComment)\n#set($relationFieldLabel = $field.fieldComment)\n#else\n#set($relationFieldLabel = $field.attrName)\n#end\n#break\n#end\n#end\n#set($fieldDict=[])\n#foreach($field in $formList)\n#if($field.fieldDict)\n#set($void=$fieldDict.add($field.fieldDict))\n#end\n#end\n<template>\n <el-dialog :title=\"form.${pk.attrName} ? \'编辑\' : \'新增\'\" v-model=\"visible\"\n :close-on-click-modal=\"false\" draggable>\n <el-form ref=\"dataFormRef\" :model=\"form\" :rules=\"dataRules\" label-width=\"90px\" v-loading=\"loading\">\n <el-row :gutter=\"24\">\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n <el-form-item label=\"${relationFieldLabel}\" prop=\"${childField}\">\n <el-tree-select\n v-model=\"form.${childField}\"\n :data=\"treeNodes\"\n :props=\"treeSelectProps\"\n check-strictly\n :render-after-expand=\"false\"\n placeholder=\"请选择${relationFieldLabel}\"\n style=\"width: 100%\"\n clearable\n filterable\n />\n </el-form-item>\n </el-col>\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName} && $field.attrName != ${childField})\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n#if($field.formType == \'text\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input type=\"textarea\" v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"form.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option :value=\"item.value\" :label=\"item.label\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\"></el-option>\n#else\n <el-option label=\"请选择\" value=\"0\"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" border :key=\"index\">{{ item.label }}</el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'checkbox\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-checkbox-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-checkbox :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\">{{ item.label }}</el-checkbox>\n#else\n <el-checkbox label=\"启用\" name=\"type\"></el-checkbox>\n <el-checkbox label=\"禁用\" name=\"type\"></el-checkbox>\n#end\n </el-checkbox-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"date\" placeholder=\"请选择#if(${field.fieldComment})${field.fiLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (1,'ControllerV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','ControllerV202611','## 查询条件按需导入:字符串字段和组织关键字用 StrUtil,其余类型用 Objects;复选框与日期范围两者都不用\n#set($needStrUtil = false)\n#set($needObjects = false)\n#foreach($field in $queryList)\n#if($field.formType == \'checkbox\' || $field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#elseif($field.formType.startsWith(\'org-\') || $field.attrType == \'String\')\n#set($needStrUtil = true)\n#else\n#set($needObjects = true)\n#end\n#end\n#set($hasOrgQuery = false)\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrgQuery = true)\n#end\n#end\n## 组织字段以 [{id,name}] JSON 存储,按 ${attrName}Keyword 对整列模糊查询,分页和导出共用\n#macro(orgKeywordParams)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), @RequestParam(required = false) String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordDecl)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordArgs)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordFilters)\n#if($hasOrgQuery)\n // 组织字段对整列 JSON 做 LIKE:关键字为数字或 id/name 时会误命中,需精确匹配时改用数据库 JSON 函数\n#end\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n wrapper.like(StrUtil.isNotBlank(${field.attrName}Keyword), ${ClassName}Entity::$str.getProperty($field.attrName), ${field.attrName}Keyword);\n#end\n#end\n#end\n#set($permissionPrefix = \"${moduleName}_${functionName}\")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\npackage ${package}.${moduleName}.controller;\n\n#if($needStrUtil)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n#if($needObjects)\nimport java.util.Objects;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"${permissionPrefix}_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'${permissionPrefix}_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}#orgKeywordParams()) {\n return R.ok(${className}Service.page(page, buildQueryWrapper(${className}#orgKeywordArgs())));\n }\n\n /**\n * 列表与导出共用的查询条件,保证导出结果与列表一致\n */\n private LambdaQueryWrapper<${ClassName}Entity> buildQueryWrapper(${ClassName}Entity ${className}#orgKeywordDecl()) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (2,'ServiceV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','ServiceV202611','package ${package}.${moduleName}.service;\n\nimport com.baomidou.mybatisplus.spring.service.IService;\nimport ${entityClassName};\n\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (3,'ServiceImplV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','ServiceImplV202611','package ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (4,'实体V202611','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','实体V202611','#set($hasOrg = false)\n#foreach($field in $fieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\npackage ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.spring.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($hasOrg)\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport ${package}.common.core.util.OrgRef;\nimport org.apache.fesod.sheet.annotation.ExcelIgnore;\nimport java.util.List;\n#end\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\n#if(!$import.startsWith(\'java.lang.\'))\nimport $import;\n#end\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(value = \"${tableName}\"#if($hasOrg), autoResultMap = true#end)\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.formType.startsWith(\'org-\'))\n @TableField(typeHandler = JacksonTypeHandler.class#if($field.autoFill == \'INSERT\' || $field.autoFill == \'INSERT_UPDATE\' || $field.autoFill == \'UPDATE\'), fill = FieldFill.${field.autoFill}#end)\n @ExcelIgnore\n#elseif($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType.startsWith(\'org-\'))\n private List<OrgRef> $field.attrName;\n#elseif($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (5,'MapperV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV202611','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (6,'Mapper.xmlV202611','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV202611','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"#if($field.formType.startsWith(\'org-\')) typeHandler=\"com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler\"#end/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (7,'权限菜单V202611','${backendPath}/menu/${functionName}_menu.sql','权限菜单V202611','#set($permissionPrefix = \"${moduleName}_${functionName}\")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\n-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'${permissionPrefix}_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'${permissionPrefix}_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'${permissionPrefix}_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'${permissionPrefix}_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'${permissionPrefix}_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (8,'api.tsV202611','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV202611','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n\n#if($ChildClassName)\n// ========== 子表相关接口 ==========\n\n/**\n * 删除子表数据\n * @param ids - 要删除的子表数据ID数组\n * @returns Promise<操作结果>\n */\nexport function delChildObj(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/child\',\n method: \'delete\',\n data: ids\n })\n}\n#end\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (9,'表格V202611','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','表格V202611','## 有字典字段时才导入 useDict\n#set($hasDictField = false)\n#foreach($field in $queryList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n#foreach($field in $gridList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n#set($permissionPrefix = "${moduleName}_${functionName}")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\n<template>\n <div class="layout-padding">\n <div class="layout-padding-auto layout-padding-view">\n#if($queryList)\n <!-- 查询表单区域 -->\n <el-row v-show="showSearch">\n <el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))#set($queryKey = "${field.attrName}Keyword")#else#set($queryKey = $field.attrName)#end\n#if($field.queryFormType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-select v-model="state.queryForm.${field.attrName}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option \n :label="item.label" \n :value="item.value" \n v-for="(item, index) in ${field.fieldDict}" \n :key="index"\n />\n#else\n <el-option label="请选择" value="0" />\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-date-picker\n type="date"\n placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"\n v-model="state.queryForm.${field.attrName}"\n :value-format="dateStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}Range">\n <el-date-picker\n type="daterange"\n range-separator="至"\n start-placeholder="开始日期"\n end-placeholder="结束日期"\n v-model="state.queryForm.${field.attrName}Range"\n :value-format="dateStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetime\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-date-picker\n type="datetime"\n placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"\n v-model="state.queryForm.${field.attrName}"\n :value-format="dateTimeStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetimerange\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}Range">\n <el-date-picker\n type="datetimerange"\n range-separator="至"\n start-placeholder="开始时间"\n end-placeholder="结束时间"\n v-model="state.queryForm.${field.attrName}Range"\n :value-format="dateTimeStr"\n />\n </el-form-item>\n#elseif($field.formType == \'radio\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-radio-group v-model="state.queryForm.${field.attrName}">\n#if($field.fieldDict)\n <el-radio \n :label="item.value" \n v-for="(item, index) in ${field.fieldDict}" \n border \n :key="index"\n >\n {{ item.label }}\n </el-radio>\n#else\n <el-radio label="${field.fieldComment}" border>\n ${field.fieldComment}\n </el-radio>\n#end\n </el-rLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (10,'表单V202611','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','表单V202611','## 有字典字段时才导入 useDict\n#set($hasDictField = false)\n#foreach($field in $formList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n#set($hasDuplicate = false)\n#foreach($field in $formList)\n#if($field.formValidator == \'duplicate\')\n#set($hasDuplicate = true)\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n#set($hasOrg = false)\n#foreach($field in $formList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\n<template>\n <el-dialog :title="form.${pk.attrName} ? \'编辑\' : \'新增\'" v-model="visible"\n :close-on-click-modal="false" draggable>\n <el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="90px" v-loading="loading">\n <el-row :gutter="24">\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName})\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n#if($field.formType.startsWith(\'org-\'))\n#set($orgType = $field.formType.replace(\'org-\', \'\').replace(\'-multiple\', \'\'))\n <el-form-item label="${field.fieldComment}" prop="${field.attrName}">\n <OrgSelect v-model="form.${field.attrName}" value-type="item" type="${orgType}" :multiple="#if($field.formType.endsWith(\'-multiple\'))true#else false#end" label="${field.fieldComment}" :disabled="loading" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'switch\')\n#switchValues($field)\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-switch :model-value="String(form.${field.attrName}) === String($switchOn)" @change="(checked) => (form.${field.attrName} = checked ? $switchOn : $switchOff)" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'text\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input type="textarea" v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-select v-model="form.${field.attrName}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option :value="item.value" :label="item.label" v-for="(item, index) in ${field.fieldDict}" :key="index"></el-option>\n#else\n <el-option label="请选择" value="0"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-radio-group v-model="form.${field.attrName}">\n#if($field.fieldDict)\n <el-radio :label="item.value" v-for="(item, index) in ${field.fieldDict}" border :key="index">{{ item.label }}</el-radio>\n#else\n <el-radio label="${field.fieldComment}" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'checkbox\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-checkbox-group v-model="form.${field.attrName}">\n#if($field.fieldDict)\n <el-checkbox :label="item.value" v-for="(item, index) in ${field.fieldDict}" :key="index">{{ item.label }}</el-checkbox>\n#else\n <el-checkbox label="启用"Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (11,'主子ServiceV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','主子ServiceV202611','#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPk = $field)\n#end\n#end\npackage ${package}.${moduleName}.service;\n\nimport com.github.yulichang.extension.mapping.base.MPJDeepService;\nimport ${entityClassName};\n\npublic interface ${ClassName}Service extends MPJDeepService<${ClassName}Entity> {\n Boolean saveDeep(${ClassName}Entity ${className});\n\n Boolean updateDeep(${ClassName}Entity ${className});\n\n Boolean removeDeep(${pk.attrType}[] ids);\n\n Boolean removeChild(${childPk.attrType}[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (12,'主子ServiceImplV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','主子ServiceImplV202611','#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPk = $field)\n#end\n#end\npackage ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport org.springframework.transaction.annotation.Transactional;\nimport lombok.RequiredArgsConstructor;\nimport java.util.Objects;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveDeep(${ClassName}Entity ${className}) {\n baseMapper.insert(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert( ${childClassName});\n }\n\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateDeep(${ClassName}Entity ${className}) {\n baseMapper.updateById(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n#set($getChildPkName=$str.getProperty(${childPk.attrName}))\n if (Objects.isNull(${childClassName}.$getChildPkName())) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert(${childClassName});\n } else {\n ${childClassName}Mapper.updateById(${childClassName});\n }\n }\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeDeep(${pk.attrType}[] ids) {\n List<${ClassName}Entity> records = baseMapper.selectBatchIds(CollUtil.toList(ids));\n if (CollUtil.isEmpty(records)) {\n return Boolean.TRUE;\n }\n ${childClassName}Mapper.delete(Wrappers.<${ChildClassName}Entity>lambdaQuery()\n .in(${ChildClassName}Entity::$str.getProperty($childField), records.stream()\n .map(${ClassName}Entity::$str.getProperty($mainField)).collect(Collectors.toList())));\n baseMapper.deleteBatchIds(CollUtil.toList(ids));\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeChild(${childPk.attrType}[] ids) {\n ${childClassName}Mapper.deleteBatchIds(CollUtil.toList(ids));\n return Boolean.TRUE;\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (13,'主实体V202611','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','主实体V202611','#set($hasOrg = false)\n#foreach($field in $fieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\npackage ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.spring.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($hasOrg)\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport ${package}.common.core.util.OrgRef;\n#end\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\n#if(!$import.startsWith(\'java.lang.\'))\nimport $import;\n#end\n#end\nimport org.apache.fesod.sheet.annotation.ExcelIgnore;\nimport com.github.yulichang.annotation.EntityMapping;\nimport java.util.List;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(value = \"${tableName}\"#if($hasOrg), autoResultMap = true#end)\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.formType.startsWith(\'org-\'))\n @TableField(typeHandler = JacksonTypeHandler.class#if($field.autoFill == \'INSERT\' || $field.autoFill == \'INSERT_UPDATE\' || $field.autoFill == \'UPDATE\'), fill = FieldFill.${field.autoFill}#end)\n @ExcelIgnore\n#elseif($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType.startsWith(\'org-\'))\n private List<OrgRef> $field.attrName;\n#elseif($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n @TableField(typeHandler = JacksonTypeHandler.class)\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n @ExcelIgnore\n @TableField(exist = false)\n @EntityMapping(thisField = \"$mainField\", joinField = \"$childField\")\n private List<${ChildClassName}Entity> ${childClassName}List;\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (14,'MapperV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV202611','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (15,'Mapper.xmlV202611','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV202611','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"#if($field.formType.startsWith(\'org-\')) typeHandler=\"com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler\"#end/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (16,'权限菜单V202611','${backendPath}/menu/${functionName}_menu.sql','权限菜单V202611','#set($permissionPrefix = \"${moduleName}_${functionName}\")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\n-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'${permissionPrefix}_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'${permissionPrefix}_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'${permissionPrefix}_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'${permissionPrefix}_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'${permissionPrefix}_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (17,'api.tsV202611','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV202611','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n\n#if($ChildClassName)\n// ========== 子表相关接口 ==========\n\n/**\n * 删除子表数据\n * @param ids - 要删除的子表数据ID数组\n * @returns Promise<操作结果>\n */\nexport function delChildObj(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/child\',\n method: \'delete\',\n data: ids\n })\n}\n#end\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (18,'子实体V202611','${entityPath}/src/main/java/${entityPackagePath}/${ChildClassName}Entity.java','子实体V202611','#set($hasOrg = false)\n#foreach($field in $childFieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\npackage ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.spring.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($hasOrg)\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport ${package}.common.core.util.OrgRef;\nimport java.util.List;\n#end\n#set($childImports = [])\n#foreach($field in $childFieldList)\n#if($field.packageName && !$field.packageName.startsWith(\'java.lang.\') && !$childImports.contains($field.packageName))\n#set($void = $childImports.add($field.packageName))\n#end\n#end\n#foreach($import in $childImports)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(value = \"${childTableName}\"#if($hasOrg), autoResultMap = true#end)\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.formType.startsWith(\'org-\'))\n @TableField(typeHandler = JacksonTypeHandler.class#if($field.autoFill == \'INSERT\' || $field.autoFill == \'INSERT_UPDATE\' || $field.autoFill == \'UPDATE\'), fill = FieldFill.${field.autoFill}#end)\n#elseif($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType.startsWith(\'org-\'))\n private List<OrgRef> $field.attrName;\n#elseif($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (19,'主子ControllerV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','主子ControllerV202611','## 查询条件按需导入:字符串字段和组织关键字用 StrUtil,其余类型用 Objects;复选框与日期范围两者都不用\n#set($needStrUtil = false)\n#set($needObjects = false)\n#foreach($field in $queryList)\n#if($field.formType == \'checkbox\' || $field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#elseif($field.formType.startsWith(\'org-\') || $field.attrType == \'String\')\n#set($needStrUtil = true)\n#else\n#set($needObjects = true)\n#end\n#end\n#set($hasOrgQuery = false)\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrgQuery = true)\n#end\n#end\n## 组织字段以 [{id,name}] JSON 存储,按 ${attrName}Keyword 对整列模糊查询,分页和导出共用\n#macro(orgKeywordParams)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), @RequestParam(required = false) String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordDecl)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordArgs)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordFilters)\n#if($hasOrgQuery)\n // 组织字段对整列 JSON 做 LIKE:关键字为数字或 id/name 时会误命中,需精确匹配时改用数据库 JSON 函数\n#end\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n wrapper.like(StrUtil.isNotBlank(${field.attrName}Keyword), ${ClassName}Entity::$str.getProperty($field.attrName), ${field.attrName}Keyword);\n#end\n#end\n#end\n#set($permissionPrefix = \"${moduleName}_${functionName}\")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPk = $field)\n#end\n#end\npackage ${package}.${moduleName}.controller;\n\n#if($needStrUtil)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n#if($needObjects)\nimport java.util.Objects;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"${permissionPrefix}_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'${permissionPrefix}_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}#orgKeywordParams()) {\n return R.ok(${className}Service.page(page, buildQueryWrapper(${className}#orgKeywordArgs())));\n }\n\n /**\n * 列表与导出共用的查询条件,保证导出结果与列表一致\n */\n private LambdaQueryWrapper<${ClassName}Entity> buildQueryWrapper(${ClassName}Entity ${className}#orgKeywordDecl()) {\Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (20,'主子表单V202611','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','主子表单V202611','## 有字典字段时才导入 useDict\n#set($hasDictField = false)\n#foreach($field in $formList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n#set($hasDuplicate = false)\n#foreach($field in $formList)\n#if($field.formValidator == \'duplicate\')\n#set($hasDuplicate = true)\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n#set($hasOrg = false)\n#foreach($field in $formList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\n#foreach($field in $childFieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\n<template>\n <el-drawer :title="form.${pk.attrName} ? (detail ? \'详情\' : \'编辑\') : \'添加\'" v-model="visible" size="50%">\n <el-form ref="dataFormRef" :model="form" :rules="dataRules" :disabled="detail" v-loading="loading">\n <el-row :gutter="24">\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName})\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n#if($field.formType.startsWith(\'org-\'))\n#set($orgType = $field.formType.replace(\'org-\', \'\').replace(\'-multiple\', \'\'))\n <el-form-item label="${field.fieldComment}" prop="${field.attrName}">\n <OrgSelect v-model="form.${field.attrName}" value-type="item" type="${orgType}" :multiple="#if($field.formType.endsWith(\'-multiple\'))true#else false#end" label="${field.fieldComment}" :disabled="loading || detail" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'switch\')\n#switchValues($field)\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-switch :model-value="String(form.${field.attrName}) === String($switchOn)" @change="(checked) => (form.${field.attrName} = checked ? $switchOn : $switchOff)" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'text\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end" prop="${field.attrName}">\n <el-input v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end" prop="${field.attrName}">\n <el-input type="textarea" v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end" prop="${field.attrName}">\n <el-select v-model="form.${field.attrName}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end">\n#if($field.fieldDict)\n <el-option :value="item.value" :label="item.label" v-for="(item, index) in ${field.fieldDict}" :key="index"></el-option>\n#else\n <el-option label="请选择" value="0"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end" prop="${field.attrName}">\n <el-radio-group v-model="form.${field.attrName}">\n#if($field.fieldDict)\n <el-radio :label="item.value" v-for="(item, index) in ${field.fieldDict}" border :key="index">{{ item.label }}</el-radio>\n#else\n <el-radio label="${field.fieldComment}" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'checkbox\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end" prop="${field.attrName}">\n <el-checkbox-group v-model="form.${field.attrName}">\n#if($field.fieldDict)\n <el-checkbox :label="item.value" v-fLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (21,'主子表格V202611','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','主子表格V202611','## 有字典字段时才导入 useDict\n#set($hasDictField = false)\n#foreach($field in $queryList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n#foreach($field in $gridList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n#set($permissionPrefix = "${moduleName}_${functionName}")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\n<template>\n <div class="layout-padding">\n <div class="layout-padding-auto layout-padding-view">\n#if($queryList)\n <el-row v-show="showSearch">\n <el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))#set($queryKey = "${field.attrName}Keyword")#else#set($queryKey = $field.attrName)#end\n#if($field.queryFormType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-select v-model="state.queryForm.${field.attrName}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option \n :label="item.label" \n :value="item.value" \n v-for="(item, index) in ${field.fieldDict}" \n :key="index">\n </el-option>\n#else\n <el-option label="请选择">0</el-option>\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-date-picker\n type="date"\n placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"\n :value-format="dateStr"\n v-model="state.queryForm.${field.attrName}"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}Range">\n <el-date-picker\n type="daterange"\n range-separator="至"\n start-placeholder="开始日期"\n end-placeholder="结束日期"\n v-model="state.queryForm.${field.attrName}Range"\n :value-format="dateStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetime\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-date-picker\n type="datetime"\n placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"\n :value-format="dateTimeStr"\n v-model="state.queryForm.${field.attrName}"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetimerange\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}Range">\n <el-date-picker\n type="datetimerange"\n range-separator="至"\n start-placeholder="开始时间"\n end-placeholder="结束时间"\n v-model="state.queryForm.${field.attrName}Range"\n :value-format="dateTimeStr"\n />\n </el-form-item>\n#elseif($field.formType == \'radio\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-radio-group v-model="state.queryForm.${field.attrName}">\n#if($field.fieldDict)\n <el-radio \n :label="item.value" \n v-for="(item, index) in ${field.fieldDict}" \n border \n :key="index">\n {{ item.label }}\n </el-radio>\n#else\n <el-radio label="${field.fieldComment}" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n#else\n <elLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (22,'子MapperV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV202611','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (23,'树形ControllerV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','树形ControllerV202611','## 查询条件按需导入:字符串字段用 StrUtil,其余类型用 Objects;复选框与日期范围两者都不用\n#set($needStrUtil = false)\n#set($needObjects = false)\n#foreach($field in $queryList)\n#if($field.formType == \'checkbox\' || $field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#elseif($field.formType.startsWith(\'org-\') || $field.attrType == \'String\')\n#set($needStrUtil = true)\n#else\n#set($needObjects = true)\n#end\n#end\n#set($hasOrgQuery = false)\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrgQuery = true)\n#end\n#end\n## 组织字段以 [{id,name}] JSON 存储,按 ${attrName}Keyword 对整列模糊查询,分页和导出共用\n#macro(orgKeywordParams)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), @RequestParam(required = false) String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordDecl)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordArgs)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordFilters)\n#if($hasOrgQuery)\n // 组织字段对整列 JSON 做 LIKE:关键字为数字或 id/name 时会误命中,需精确匹配时改用数据库 JSON 函数\n#end\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n wrapper.like(StrUtil.isNotBlank(${field.attrName}Keyword), ${ClassName}Entity::$str.getProperty($field.attrName), ${field.attrName}Keyword);\n#end\n#end\n#end\npackage ${package}.${moduleName}.controller;\n\n#if($queryList)\n#if($needStrUtil)\nimport cn.hutool.core.util.StrUtil;\n#end\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n#if($needObjects)\nimport java.util.Objects;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping("/${functionName}" )\n@Tag(description = "${functionName}" , name = "${tableComment}管理" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 获取树形列表\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = "获取树形列表" , description = "获取树形列表" )\n @GetMapping("/tree" )\n #if($isSpringBoot3)\n @HasPermission("$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view")\n #else\n @PreAuthorize("@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')" )\n #end\n public R get${ClassName}Tree(@ParameterObject ${ClassName}Entity ${className}#orgKeywordParams()) {\n return R.ok(${className}Service.buildTree(buildQueryWrapper(${className}#orgKeywordArgs())));\n }\n\n /**\n * 列表与导出共用的查询条件,保证导出结果与列表一致\n */\n private LambdaQueryWrapper<${ClassName}Entity> buildQueryWrapper(${ClassName}Entity ${className}#orgKeywordDecl()) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#orgKeywordFilters()\n#foreach ($field in $queryList)\n#if(!$field.formType.startsWith(\'org-\'))\n#set($getAttrName=$str.getProperty($field.attLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (24,'树形ServiceV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','树形ServiceV202611','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.spring.service.IService;\nimport ${entityClassName};\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List<Tree<${pk.attrType}>> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper);\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n boolean removeBatchByIds(List<${pk.attrType}> ids);\n} ','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (25,'树形ServiceImplV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','树形ServiceImplV202611','## 根节点 ID 按主键类型生成,与 Tree<${pk.attrType}> 类型一致\n#if($pk.attrType == \'Long\')\n#set($treeRootId = \'0L\')\n#elseif($pk.attrType == \'String\')\n#set($treeRootId = \'"0"\')\n#else\n#set($treeRootId = \'0\')\n#end\npackage ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List<Tree<${pk.attrType}>> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper) {\n // 查询命中节点,再按层补齐祖先,保留完整树路径\n List<${ClassName}Entity> allList = list(wrapper);\n \n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n Map<${pk.attrType}, ${ClassName}Entity> nodes = new LinkedHashMap<>();\n allList.forEach(entity -> nodes.put(entity.$str.getProperty($pk.attrName)(), entity));\n List<${ClassName}Entity> level = allList;\n while (!level.isEmpty()) {\n List<${pk.attrType}> parentIds = level.stream()\n .map(${ClassName}Entity::$str.getProperty($parentField))\n .filter(id -> id != null && !Objects.equals(id, ${treeRootId}) && !nodes.containsKey(id))\n .distinct()\n .toList();\n if (parentIds.isEmpty()) {\n break;\n }\n level = listByIds(parentIds);\n level.forEach(entity -> nodes.put(entity.$str.getProperty($pk.attrName)(), entity));\n }\n\n // 转换为TreeNode,仅包含命中节点及其祖先\n List<TreeNode<${pk.attrType}>> collect = nodes.values().stream().map(getNodeFunction()).toList();\n\n // 使用TreeUtil构建树形结构,根节点ID为0\n return TreeUtil.build(collect, ${treeRootId});\n }\n\n /**\n * 获取TreeNode转换函数\n * @return TreeNode转换函数\n */\n @NotNull\n private Function<${ClassName}Entity, TreeNode<${pk.attrType}>> getNodeFunction() {\n return entity -> {\n TreeNode<${pk.attrType}> node = new TreeNode<>();\n node.setId(entity.$str.getProperty($pk.attrName)());\n node.setName(entity.$str.getProperty($nameField)());\n node.setParentId(entity.$str.getProperty($parentField)() != null ? entity.$str.getProperty($parentField)() : ${treeRootId});\n\n // 扩展属性\n Map<String, Object> extra = new HashMap<>();\n#foreach($field in $fieldList)\n extra.put(${ClassName}Entity.Fields.${field.attrName}, entity.$str.getProperty($field.attrName)());\n#end\n node.setExtra(extra);\n return node;\n };\n }\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public boolean removeBatchByIds(List<${pk.attrType}> ids) {\n if (CollUtil.isEmpty(ids)) {\n return true;\n }\n\n // 获取所有要删除的ID(包括子节点)\n List<${pk.attrType}> allDeleteIds = new ArrayList<>(ids);\n \n for (${pk.attrType} id : ids) {\n List<${pk.attrType}> childIds = getChildIdsRecursive(id);\n allDeleteIds.addAll(childIds);\n }\n\n // 去重\n allLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (26,'树形实体V202611','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','树形实体V202611','#set($hasOrg = false)\n#foreach($field in $fieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\npackage ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.spring.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.experimental.FieldNameConstants;\n#if($hasOrg)\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport ${package}.common.core.util.OrgRef;\nimport org.apache.fesod.sheet.annotation.ExcelIgnore;\nimport java.util.List;\n#end\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\n#if(!$import.startsWith(\'java.lang.\'))\nimport $import;\n#end\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n@FieldNameConstants\n#if($isTenant)\n@TenantTable\n#end\n@TableName(value = "${tableName}"#if($hasOrg), autoResultMap = true#end)\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = "${tableComment}")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.formType.startsWith(\'org-\'))\n @TableField(typeHandler = JacksonTypeHandler.class#if($field.autoFill == \'INSERT\' || $field.autoFill == \'INSERT_UPDATE\' || $field.autoFill == \'UPDATE\'), fill = FieldFill.${field.autoFill}#end)\n @ExcelIgnore\n#elseif($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description="$comment"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType.startsWith(\'org-\'))\n private List<OrgRef> $field.attrName;\n#elseif($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description="$comment 查询范围",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (27,'MapperV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV202611','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (28,'Mapper.xmlV202611','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV202611','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"#if($field.formType.startsWith(\'org-\')) typeHandler=\"com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler\"#end/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (29,'权限菜单V202611','${backendPath}/menu/${functionName}_menu.sql','权限菜单V202611','#set($permissionPrefix = \"${moduleName}_${functionName}\")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\n-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'${permissionPrefix}_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'${permissionPrefix}_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'${permissionPrefix}_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'${permissionPrefix}_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'${permissionPrefix}_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (30,'树形api.tsV202611','${frontendPath}/src/api/${moduleName}/${functionName}.ts','树形api.tsV202611','import request from \"/@/utils/request\"\n\n// ========== 树形表格CRUD接口 ==========\n\n/**\n * 获取树形列表数据\n * @param query - 查询参数对象\n * @returns Promise<树形数据>\n */\nexport function fetchTreeList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据(递归删除子节点)\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (31,'树形表格V202611','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','树形表格V202611','## 有字典字段时才导入 useDict\n#set($hasDictField = false)\n#foreach($field in $queryList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n#foreach($field in $gridList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n<template>\n <div class="layout-padding">\n <div class="layout-padding-auto layout-padding-view">\n#if($queryList)\n <!-- 查询表单区域 -->\n <el-row v-show="showSearch">\n <el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))#set($queryKey = "${field.attrName}Keyword")#else#set($queryKey = $field.attrName)#end\n#if($field.queryFormType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${queryKey}">\n <el-select v-model="state.queryForm.${queryKey}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option \n :label="item.label" \n :value="item.value" \n v-for="(item, index) in ${field.fieldDict}" \n :key="index"\n />\n#else\n <el-option label="请选择" value="0" />\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${queryKey}">\n <el-date-picker \n type="date" \n placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" \n v-model="state.queryForm.${queryKey}"\n :value-format="dateStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetime\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${queryKey}">\n <el-date-picker\n type="datetime"\n placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"\n v-model="state.queryForm.${queryKey}"\n :value-format="dateTimeStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}Range">\n <el-date-picker\n type="daterange"\n range-separator="至"\n start-placeholder="开始日期"\n end-placeholder="结束日期"\n v-model="state.queryForm.${field.attrName}Range"\n :value-format="dateStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetimerange\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}Range">\n <el-date-picker\n type="datetimerange"\n range-separator="至"\n start-placeholder="开始时间"\n end-placeholder="结束时间"\n v-model="state.queryForm.${field.attrName}Range"\n :value-format="dateTimeStr"\n />\n </el-form-item>\n#elseif($field.formType == \'radio\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${queryKey}">\n <el-radio-group v-model="state.queryForm.${queryKey}">\n#if($field.fieldDict)\n <el-radio \n :label="item.value" \n v-for="(item, index) in ${field.fieldDict}" \n border \n :key="index"\n >\n {{ item.label }}\n </el-radio>\n#else\n <el-radio label="${field.fieldComment}" border>\n ${field.fieldComment}\n </el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n#else\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#enLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (32,'树形表单V202611','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','树形表单V202611','#set($hasDuplicate = false)\n#foreach($field in $formList)\n#if($field.formValidator == \'duplicate\')\n#set($hasDuplicate = true)\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n#set($hasOrg = false)\n#foreach($field in $formList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\n<template>\n <el-dialog :title="form.${pk.attrName} ? \'编辑\' : \'新增\'" v-model="visible"\n :close-on-click-modal="false" draggable>\n <el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">\n <el-row :gutter="24">\n <!-- 父级节点选择 -->\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n <el-form-item label="父级节点" prop="${parentField}">\n <el-tree-select\n v-model="form.${parentField}"\n :data="parentNodes"\n :props="treeSelectProps"\n check-strictly\n :render-after-expand="false"\n placeholder="请选择父级节点"\n style="width: 100%"\n clearable\n filterable\n />\n </el-form-item>\n </el-col>\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName} && $field.attrName != ${parentField})\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n#if($field.formType.startsWith(\'org-\'))\n#set($orgType = $field.formType.replace(\'org-\', \'\').replace(\'-multiple\', \'\'))\n <el-form-item label="${field.fieldComment}" prop="${field.attrName}">\n <OrgSelect v-model="form.${field.attrName}" value-type="item" type="${orgType}" :multiple="#if($field.formType.endsWith(\'-multiple\'))true#else false#end" label="${field.fieldComment}" :disabled="loading" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'switch\')\n#switchValues($field)\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-switch :model-value="String(form.${field.attrName}) === String($switchOn)" @change="(checked) => (form.${field.attrName} = checked ? $switchOn : $switchOff)" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'text\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input type="textarea" v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-select v-model="form.${field.attrName}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option :value="item.value" :label="item.label" v-for="(item, index) in ${field.fieldDict}" :key="index"></el-option>\n#else\n <el-option label="请选择" value="0"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-radio-group v-model="form.${field.attrName}">\n#if($field.fieldDict)\n <el-radio :label="item.value" v-for="(item, index) in ${field.fieldDict}" border :key="index">{{ item.label }}</el-radio>\n#else\n <el-radio label="${field.fieldComment}" bordLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (33,'左树右表ControllerV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','左树右表ControllerV202611','## 查询条件按需导入 StrUtil(字符串字段);Objects 用于树节点筛选,始终导入\n#set($needStrUtil = false)\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\') || ($field.formType != \'checkbox\' && $field.queryFormType != \'daterange\' && $field.queryFormType != \'datetimerange\' && $field.attrType == \'String\'))\n#set($needStrUtil = true)\n#end\n#end\n#set($hasOrgQuery = false)\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrgQuery = true)\n#end\n#end\n## 组织字段以 [{id,name}] JSON 存储,按 ${attrName}Keyword 对整列模糊查询,分页和导出共用\n#macro(orgKeywordParams)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), @RequestParam(required = false) String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordDecl)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordArgs)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordFilters)\n#if($hasOrgQuery)\n // 组织字段对整列 JSON 做 LIKE:关键字为数字或 id/name 时会误命中,需精确匹配时改用数据库 JSON 函数\n#end\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n wrapper.like(StrUtil.isNotBlank(${field.attrName}Keyword), ${ClassName}Entity::$str.getProperty($field.attrName), ${field.attrName}Keyword);\n#end\n#end\n#end\npackage ${package}.${moduleName}.controller;\n\n#if($needStrUtil)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($childFieldGetter = $str.getProperty($childField))\n@RestController\n@RequiredArgsConstructor\n@RequestMapping("/${functionName}" )\n@Tag(description = "${functionName}" , name = "${tableComment}管理" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = "分页查询" , description = "分页查询" )\n @GetMapping("/page" )\n #if($isSpringBoot3)\n @HasPermission("$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view")\n #else\n @PreAuthorize("@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}#orgKeywordParams()) {\n return R.ok(${className}Service.page(page, buildQueryWrapper(${className}#orgKeywordArgs())));\n }\n\n /**\n * 列表与导出共用的查询条件,保证导出结果与列表一致\n */\n private LambdaQueryWrapper<${ClassName}Entity> buildQueryWrapper(${ClassName}Entity ${className}#orgKeywordDecl()) {\n LambLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (34,'左树右表ServiceV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','左树右表ServiceV202611','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.spring.service.IService;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List<Tree<${childPkField.attrType}>> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper);\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName});\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n Boolean saveTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n Boolean updateTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n Boolean removeTreeByIds(${childPkField.attrType}[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (35,'左树右表ServiceImplV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','左树右表ServiceImplV202611','package ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#set($childPkGetter = $str.getProperty($childPkField.attrName))\n#break\n#end\n#end\n#set($parentGetter = $str.getProperty($parentField))\n#set($childFieldGetter = $str.getProperty($childField))\n#if($childPkField.attrType == \'Long\')\n#set($treeRootId = \'0L\')\n#elseif($childPkField.attrType == \'String\')\n#set($treeRootId = \'\"0\"\')\n#else\n#set($treeRootId = \'0\')\n#end\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List<Tree<${childPkField.attrType}>> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper) {\n List<${ChildClassName}Entity> allList = ${childClassName}Mapper.selectList(wrapper);\n\n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n List<TreeNode<${childPkField.attrType}>> collect = allList.stream().map(getNodeFunction()).toList();\n return TreeUtil.build(collect, ${treeRootId});\n }\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n @Override\n public List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName}) {\n return ${childClassName}Mapper.selectList(Wrappers.query(${childClassName}));\n }\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(null, ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.insert(${childClassName}) > 0;\n }\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(${childClassName}.$str.getProperty($childPkField.attrName)(), ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.updateById(${childClassName}) > 0;\n }\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeTreeByIds(${childPkField.attrType}[] ids) {\n if (ArrayUtil.isEmpty(ids)) {\n return Boolean.TRUE;\n }\n\n for (${childPkField.attrType} id : ids) {\n if (hasChildNodes(id)) {\n throw new RuntimeException(\"当前节点存在子节点,不能删除\");\n }\n if (hasMainRecords(id)) {\n throw new RuntimeException(\"当前节点已关联主表数据,不能删除\");\n }\n }\n\n return ${childClassNameLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (36,'实体V202611','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','实体V202611','#set($hasOrg = false)\n#foreach($field in $fieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\npackage ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.spring.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($hasOrg)\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport ${package}.common.core.util.OrgRef;\nimport org.apache.fesod.sheet.annotation.ExcelIgnore;\nimport java.util.List;\n#end\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\n#if(!$import.startsWith(\'java.lang.\'))\nimport $import;\n#end\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(value = \"${tableName}\"#if($hasOrg), autoResultMap = true#end)\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.formType.startsWith(\'org-\'))\n @TableField(typeHandler = JacksonTypeHandler.class#if($field.autoFill == \'INSERT\' || $field.autoFill == \'INSERT_UPDATE\' || $field.autoFill == \'UPDATE\'), fill = FieldFill.${field.autoFill}#end)\n @ExcelIgnore\n#elseif($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType.startsWith(\'org-\'))\n private List<OrgRef> $field.attrName;\n#elseif($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (37,'MapperV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV202611','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (38,'Mapper.xmlV202611','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV202611','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"#if($field.formType.startsWith(\'org-\')) typeHandler=\"com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler\"#end/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (39,'子实体V202611','${entityPath}/src/main/java/${entityPackagePath}/${ChildClassName}Entity.java','子实体V202611','#set($hasOrg = false)\n#foreach($field in $childFieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\npackage ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.spring.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($hasOrg)\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport ${package}.common.core.util.OrgRef;\nimport java.util.List;\n#end\n#set($childImports = [])\n#foreach($field in $childFieldList)\n#if($field.packageName && !$field.packageName.startsWith(\'java.lang.\') && !$childImports.contains($field.packageName))\n#set($void = $childImports.add($field.packageName))\n#end\n#end\n#foreach($import in $childImports)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(value = \"${childTableName}\"#if($hasOrg), autoResultMap = true#end)\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.formType.startsWith(\'org-\'))\n @TableField(typeHandler = JacksonTypeHandler.class#if($field.autoFill == \'INSERT\' || $field.autoFill == \'INSERT_UPDATE\' || $field.autoFill == \'UPDATE\'), fill = FieldFill.${field.autoFill}#end)\n#elseif($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType.startsWith(\'org-\'))\n private List<OrgRef> $field.attrName;\n#elseif($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (40,'子MapperV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV202611','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (41,'权限菜单V202611','${backendPath}/menu/${functionName}_menu.sql','权限菜单V202611','#set($permissionPrefix = \"${moduleName}_${functionName}\")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\n-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'${permissionPrefix}_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'${permissionPrefix}_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'${permissionPrefix}_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'${permissionPrefix}_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'${permissionPrefix}_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (42,'左树右表api.tsV202611','${frontendPath}/src/api/${moduleName}/${functionName}.ts','左树右表api.tsV202611','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 树形CRUD接口 ==========\n\n/**\n * 获取树形列表数据\n * @param query - 查询参数对象\n * @returns Promise<树形数据>\n */\nexport function fetchTreeList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 获取树节点详情\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<树节点详情>\n */\nexport function getTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 新增树节点\n * @param obj - 要新增的树节点对象\n * @returns Promise<操作结果>\n */\nexport function addTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 更新树节点\n * @param obj - 要更新的树节点对象\n * @returns Promise<操作结果>\n */\nexport function putTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'put\',\n data: obj\n })\n}\n\n/**\n * 删除树节点\n * @param ids - 要删除的树节点ID数组\n * @returns Promise<操作结果>\n */\nexport function delTreeObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'delete\',\n data: ids\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n *\n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (43,'左树右表表格V202611','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','左树右表表格V202611','## 有字典字段时才导入 useDict\n#set($hasDictField = false)\n#foreach($field in $queryList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n#foreach($field in $gridList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n<template>\n <div class="layout-padding">\n <splitpanes>\n <pane size="15">\n <div class="layout-padding-auto layout-padding-view">\n <el-scrollbar>\n <div class="mb8" style="display: flex; align-items: center; justify-content: space-between;">\n <span style="font-weight: 500;">左侧节点</span>\n <div>\n <el-tooltip :content="currentTreeId ? \'新增子节点\' : \'新增根节点\'" placement="top">\n <el-button link type="primary" icon="FolderAdd" @click="handleAddTree" />\n </el-tooltip>\n </div>\n </div>\n <el-tree\n ref="treeRef"\n :data="treeData"\n :props="treeProps"\n node-key="${childPkField.attrName}"\n highlight-current\n default-expand-all\n :expand-on-click-node="false"\n @node-click="handleTreeNodeClick"\n >\n <template #default="{ node, data }">\n <span style="flex: 1; display: flex; align-items: center; justify-content: space-between;">\n <span>{{ node.label }}</span>\n <span v-if="currentTreeId === data.${childPkField.attrName}" style="margin-left: 8px;">\n <el-tooltip content="编辑" placement="top">\n <el-button link type="primary" icon="Edit" size="small" @click.stop="handleEditTree" />\n </el-tooltip>\n <el-tooltip content="删除" placement="top">\n <el-button link type="danger" icon="Delete" size="small" @click.stop="handleDeleteTree" />\n </el-tooltip>\n </span>\n </span>\n </template>\n </el-tree>\n </el-scrollbar>\n </div>\n </pane>\n <pane class="ml8">\n <div class="layout-padding-auto layout-padding-view">\n#if($queryList)\n <el-row v-show="showSearch">\n <el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))#set($queryKey = "${field.attrName}Keyword")#else#set($queryKey = $field.attrName)#end\n#if($field.attrName != ${childField})\n#if($field.queryFormType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${queryKey}">\n <el-select v-model="state.queryForm.${queryKey}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option \n :label="item.label" \n :value="item.value" \n v-for="(item, index) in ${field.fieldDict}" \n :key="index"\n />\n#else\n <el-option label="请选择" value="0" />\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${queryKey}">\n <el-date-picker\n type="date"\n placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"\n v-model="state.queryForm.${queryKey}"\n :value-format="dateStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}Range">\n <el-date-pickerLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (44,'左树右表树表单V202611','${frontendPath}/src/views/${moduleName}/${functionName}/tree-form.vue','左树右表树表单V202611','#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($fieldDict=[])\n#foreach($field in $childFieldList)\n#if($field.fieldDict)\n#set($void=$fieldDict.add($field.fieldDict))\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n#set($hasOrg = false)\n#foreach($field in $childFieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\n<template>\n <el-dialog :title="form.${childPkField.attrName} ? \'编辑\' : \'新增\'" v-model="visible"\n :close-on-click-modal="false" draggable>\n <el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="90px" v-loading="loading">\n <el-row :gutter="24">\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n <el-form-item label="父级节点" prop="${parentField}">\n <el-tree-select\n v-model="form.${parentField}"\n :data="parentNodes"\n :props="treeSelectProps"\n check-strictly\n :render-after-expand="false"\n placeholder="请选择父级节点"\n style="width: 100%"\n clearable\n filterable\n />\n </el-form-item>\n </el-col>\n#foreach($field in $childFieldList)\n#if($field.attrName != ${childPkField.attrName} && $field.attrName != ${parentField})\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n#if($field.formType.startsWith(\'org-\'))\n#set($orgType = $field.formType.replace(\'org-\', \'\').replace(\'-multiple\', \'\'))\n <el-form-item label="${field.fieldComment}" prop="${field.attrName}">\n <OrgSelect v-model="form.${field.attrName}" value-type="item" type="${orgType}" :multiple="#if($field.formType.endsWith(\'-multiple\'))true#else false#end" label="${field.fieldComment}" :disabled="loading" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'switch\')\n#switchValues($field)\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-switch :model-value="String(form.${field.attrName}) === String($switchOn)" @change="(checked) => (form.${field.attrName} = checked ? $switchOn : $switchOff)" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'text\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input type="textarea" v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-select v-model="form.${field.attrName}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option :value="item.value" :label="item.label" v-for="(item, index) in ${field.fieldDict}" :key="index"></el-option>\n#else\n <el-option label="请选择" value="0"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-radio-group v-model="form.${field.attrName}">\n#if($field.fieldDict)\n <el-radio :label="item.value" v-for="(item, index) in ${field.fieldDicLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (45,'左树右表主表单V202611','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','左树右表主表单V202611','#set($hasDuplicate = false)\n#foreach($field in $formList)\n#if($field.attrName != ${childField} && $field.formValidator == \'duplicate\')\n#set($hasDuplicate = true)\n#end\n#end\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($relationFieldLabel = $childField)\n#foreach($field in $fieldList)\n#if($field.attrName == $childField)\n#if($field.fieldComment)\n#set($relationFieldLabel = $field.fieldComment)\n#else\n#set($relationFieldLabel = $field.attrName)\n#end\n#break\n#end\n#end\n#set($fieldDict=[])\n#foreach($field in $formList)\n#if($field.fieldDict)\n#set($void=$fieldDict.add($field.fieldDict))\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n#set($hasOrg = false)\n#foreach($field in $formList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\n<template>\n <el-dialog :title="form.${pk.attrName} ? \'编辑\' : \'新增\'" v-model="visible"\n :close-on-click-modal="false" draggable>\n <el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="90px" v-loading="loading">\n <el-row :gutter="24">\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n <el-form-item label="${relationFieldLabel}" prop="${childField}">\n <el-tree-select\n v-model="form.${childField}"\n :data="treeNodes"\n :props="treeSelectProps"\n check-strictly\n :render-after-expand="false"\n placeholder="请选择${relationFieldLabel}"\n style="width: 100%"\n clearable\n filterable\n />\n </el-form-item>\n </el-col>\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName} && $field.attrName != ${childField})\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n#if($field.formType.startsWith(\'org-\'))\n#set($orgType = $field.formType.replace(\'org-\', \'\').replace(\'-multiple\', \'\'))\n <el-form-item label="${field.fieldComment}" prop="${field.attrName}">\n <OrgSelect v-model="form.${field.attrName}" value-type="item" type="${orgType}" :multiple="#if($field.formType.endsWith(\'-multiple\'))true#else false#end" label="${field.fieldComment}" :disabled="loading" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'switch\')\n#switchValues($field)\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-switch :model-value="String(form.${field.attrName}) === String($switchOn)" @change="(checked) => (form.${field.attrName} = checked ? $switchOn : $switchOff)" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'text\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input type="textarea" v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-select v-model="form.${field.attrName}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option :value="item.value" :label="item.label" v-for="(item, index) in ${field.fieldDict}" :key="index"></el-option>\n#else\n <el-option label="请选择" value="0"></el-option>\n#end\n Line truncated
COMMIT;
-- ----------------------------
+131 -116
View File
@@ -112,8 +112,8 @@ CREATE TABLE `sys_dept` (
`dept_id` bigint(20) NOT NULL COMMENT '部门ID',
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '部门名称',
`sort_order` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标志',
@@ -150,8 +150,8 @@ CREATE TABLE `sys_dict` (
`id` bigint(20) NOT NULL COMMENT '编号',
`dict_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '字典类型',
`description` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '描述',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remarks` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注信息',
@@ -209,8 +209,8 @@ CREATE TABLE `sys_dict_item` (
`description` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '字典项描述',
`list_class` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '标签类型',
`sort_order` int(11) NOT NULL DEFAULT '0' COMMENT '排序(升序)',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remarks` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注信息',
@@ -262,8 +262,6 @@ INSERT INTO `sys_dict_item` VALUES (33, 11, '0', '正常', 'status_type', '状
INSERT INTO `sys_dict_item` VALUES (34, 11, '9', '冻结', 'status_type', '状态冻结', NULL, 1, ' ', ' ', '2019-05-15 16:31:56', '2019-05-16 22:30:50', '状态冻结', '0', 1);
INSERT INTO `sys_dict_item` VALUES (35, 12, '1', '系统类', 'dict_type', '系统类字典', NULL, 0, ' ', ' ', '2019-05-16 14:20:40', '2019-05-16 14:20:40', '不能修改删除', '0', 1);
INSERT INTO `sys_dict_item` VALUES (36, 12, '0', '业务类', 'dict_type', '业务类字典', NULL, 0, ' ', ' ', '2019-05-16 14:20:59', '2019-05-16 14:20:59', '可以修改', '0', 1);
INSERT INTO `sys_dict_item` VALUES (37, 2, 'GITEE', '码云', 'social_type', '码云', NULL, 2, ' ', ' ', '2019-06-28 09:59:12', '2019-06-28 09:59:12', '码云', '0', 1);
INSERT INTO `sys_dict_item` VALUES (38, 2, 'OSC', '开源中国', 'social_type', '开源中国登录', NULL, 2, ' ', ' ', '2019-06-28 10:04:32', '2019-06-28 10:04:32', '', '0', 1);
INSERT INTO `sys_dict_item` VALUES (39, 14, 'password', '密码模式', 'grant_types', '支持oauth密码模式', NULL, 0, ' ', ' ', '2019-08-13 07:35:28', '2019-08-13 07:35:28', NULL, '0', 1);
INSERT INTO `sys_dict_item` VALUES (40, 14, 'authorization_code', '授权码模式', 'grant_types', 'oauth2 授权码模式', NULL, 1, ' ', ' ', '2019-08-13 07:36:07', '2019-08-13 07:36:07', NULL, '0', 1);
INSERT INTO `sys_dict_item` VALUES (41, 14, 'client_credentials', '客户端模式', 'grant_types', 'oauth2 客户端模式', NULL, 2, ' ', ' ', '2019-08-13 07:36:30', '2019-08-13 07:36:30', NULL, '0', 1);
@@ -322,6 +320,7 @@ INSERT INTO `sys_dict_item` VALUES (95, 29, '0', '黑名单', 'sensitive_type',
INSERT INTO `sys_dict_item` VALUES (96, 29, '1', '白名单', 'sensitive_type', '失败', NULL, 3, 'admin', ' ', '2023-10-27 10:30:47', NULL, NULL, '0', 1);
INSERT INTO `sys_dict_item` VALUES (97,2,'MINI','小程序登录','social_type','小程序登录',NULL,3,' ',' ','2022-02-18 13:56:25','2022-02-18 13:56:28',NULL,'0',1);
INSERT INTO `sys_dict_item` VALUES (98,2,'clarity','clarity','social_type','clarity',NULL,3,' ',' ','2022-02-18 13:56:25','2022-02-18 13:56:28',NULL,'0',1);
INSERT INTO `sys_dict_item` VALUES (100,2,'FEISHU','企业飞书','social_type','企业飞书扫码登录',NULL,5,' ',' ','2026-09-18 00:00:00','2026-09-18 00:00:00',NULL,'0',1);
INSERT INTO `sys_dict_item` VALUES (99,2,'umami','umami','social_type','umami',NULL,4,' ',' ','2026-06-23 00:00:00','2026-06-23 00:00:00',NULL,'0',1);
COMMIT;
@@ -339,8 +338,8 @@ CREATE TABLE `sys_file` (
`type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '文件类型',
`hash` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '文件hash',
`file_size` bigint(20) DEFAULT NULL COMMENT '文件大小',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT NULL COMMENT '上传时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标志',
@@ -387,9 +386,9 @@ CREATE TABLE `sys_i18n` (
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'name',
`zh_cn` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '中文',
`en` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '英文',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记',
PRIMARY KEY (`id`)
@@ -399,80 +398,83 @@ CREATE TABLE `sys_i18n` (
-- Records of sys_i18n
-- ----------------------------
BEGIN;
INSERT INTO `sys_i18n` VALUES (1, 'router.permissionManagement', '权限管理', 'Permission Management', '', '2023-02-14 02:03:59', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (2, 'router.userManagement', '用户管理', 'User Management', 'admin', '2023-02-14 10:39:08', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (3, 'router.menuManagement', '菜单管理', 'Menu Management', 'admin', '2023-02-15 23:14:39', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (4, 'router.roleManagement', '角色管理', 'Role Management', 'admin', '2023-02-15 23:15:51', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (5, 'router.departmentManagement', '部门管理', 'Department Management', 'admin', '2023-02-15 23:16:52', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (6, 'router.tenantManagement', '租户管理', 'Tenant Management', 'admin', '2023-02-24 10:08:29', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (7, 'router.postManagement', '岗位管理', 'Post Management', 'admin', '2023-02-24 10:12:58', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (8, 'router.systemManagement', '系统管理', 'System Management', 'admin', '2023-02-24 10:13:34', 'admin', '2023-02-24 10:58:30', '0');
INSERT INTO `sys_i18n` VALUES (9, 'router.operationLog', '操作日志', 'Operation Log', 'admin', '2023-02-24 10:14:47', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (10, 'router.dictManagement', '字典管理', 'Dictionary Management', 'admin', '2023-02-24 10:16:21', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (11, 'router.parameterManagement', '参数管理', 'Parameter Management', 'admin', '2023-02-24 10:17:04', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (12, 'router.codeGeneration', '代码生成', 'Code Generation', 'admin', '2023-02-24 10:19:16', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (13, 'router.terminalManagement', '终端管理', 'Terminal Management', 'admin', '2023-02-24 10:21:45', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (14, 'router.keyManagement', '密钥管理', 'Key Management', 'admin', '2023-02-24 10:22:52', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (15, 'router.tokenManagement', '令牌管理', 'Token Management', 'admin', '2023-02-24 10:23:22', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (16, 'router.quartzManagement', 'Quartz管理', 'Quartz Management', 'admin', '2023-02-24 10:24:32', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (17, 'router.metadataManagement', '元数据管理', 'Metadata Management', 'admin', '2023-02-24 10:25:11', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (1, 'router.permissionManagement', '权限管理', 'Permissions', '', '2023-02-14 02:03:59', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (2, 'router.userManagement', '用户管理', 'Users', 'admin', '2023-02-14 10:39:08', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (3, 'router.menuManagement', '菜单管理', 'Menus', 'admin', '2023-02-15 23:14:39', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (4, 'router.roleManagement', '角色管理', 'Roles', 'admin', '2023-02-15 23:15:51', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (5, 'router.departmentManagement', '部门管理', 'Departments', 'admin', '2023-02-15 23:16:52', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (6, 'router.tenantManagement', '租户管理', 'Tenants', 'admin', '2023-02-24 10:08:29', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (7, 'router.postManagement', '岗位管理', 'Positions', 'admin', '2023-02-24 10:12:58', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (8, 'router.systemManagement', '系统管理', 'System', 'admin', '2023-02-24 10:13:34', 'admin', '2023-02-24 10:58:30', '0');
INSERT INTO `sys_i18n` VALUES (9, 'router.operationLog', '操作日志', 'Operation Logs', 'admin', '2023-02-24 10:14:47', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (10, 'router.dictManagement', '字典管理', 'Dictionaries', 'admin', '2023-02-24 10:16:21', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (11, 'router.parameterManagement', '参数管理', 'Parameters', 'admin', '2023-02-24 10:17:04', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (12, 'router.codeGeneration', '代码生成', 'Code Generator', 'admin', '2023-02-24 10:19:16', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (13, 'router.terminalManagement', '终端管理', 'OAuth Clients', 'admin', '2023-02-24 10:21:45', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (14, 'router.keyManagement', '密钥管理', 'Keys', 'admin', '2023-02-24 10:22:52', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (15, 'router.tokenManagement', '令牌管理', 'Tokens', 'admin', '2023-02-24 10:23:22', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (16, 'router.quartzManagement', 'Quartz管理', 'Quartz Jobs', 'admin', '2023-02-24 10:24:32', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (17, 'router.metadataManagement', '元数据管理', 'Metadata', 'admin', '2023-02-24 10:25:11', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (18, 'router.documentExtension', '文档扩展', 'Document Extension', 'admin', '2023-02-24 10:27:23', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (19, 'router.fileManagement', '文件管理', 'File Management', 'admin', '2023-02-24 10:28:44', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (20, 'router.platformDevelopment', '开发平台', 'Platform Development', 'admin', '2023-02-24 10:29:28', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (21, 'router.dataSourceManagement', '数据源管理', 'Data Source Management', 'admin', '2023-02-24 10:30:33', 'admin', '2023-03-06 14:33:20', '0');
INSERT INTO `sys_i18n` VALUES (22, 'router.formDesign', '表单设计', 'Form Design', 'admin', '2023-02-24 10:31:33', 'admin', '2023-03-06 14:33:28', '0');
INSERT INTO `sys_i18n` VALUES (23, 'router.appManagement', 'APP管理', 'App Management', 'admin', '2023-02-24 10:33:22', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (24, 'router.customerManagement', '客户管理', 'Customer Management', 'admin', '2023-02-24 10:35:30', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (19, 'router.fileManagement', '文件管理', 'Files', 'admin', '2023-02-24 10:28:44', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (20, 'router.platformDevelopment', '开发平台', 'Development', 'admin', '2023-02-24 10:29:28', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (21, 'router.dataSourceManagement', '数据源管理', 'Data Sources', 'admin', '2023-02-24 10:30:33', 'admin', '2023-03-06 14:33:20', '0');
INSERT INTO `sys_i18n` VALUES (23, 'router.appManagement', 'APP管理', 'Apps', 'admin', '2023-02-24 10:33:22', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (24, 'router.customerManagement', '客户管理', 'Customers', 'admin', '2023-02-24 10:35:30', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (25, 'router.appRole', 'APP角色', 'App Role', 'admin', '2023-02-24 10:36:17', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (26, 'router.appPermission', 'APP权限', 'App Permission', 'admin', '2023-02-24 10:36:59', 'admin', '2023-02-24 10:37:47', '0');
INSERT INTO `sys_i18n` VALUES (27, 'router.appKey', 'APP秘钥', 'App Key', 'admin', '2023-02-24 10:36:59', 'admin', '2023-02-24 10:40:27', '0');
INSERT INTO `sys_i18n` VALUES (28, 'router.internationalizationManagement', '国际化管理', 'Internationalization Management', 'admin', '2023-02-24 10:36:59', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (29, 'router.auditLog', '审计日志', 'Audit Log', 'admin', '2023-02-24 10:36:59', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (27, 'router.appKey', 'APP秘钥', 'App Keys', 'admin', '2023-02-24 10:36:59', 'admin', '2023-02-24 10:40:27', '0');
INSERT INTO `sys_i18n` VALUES (28, 'router.internationalizationManagement', '国际化管理', 'Translations', 'admin', '2023-02-24 10:36:59', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (29, 'router.auditLog', '审计日志', 'Audit Logs', 'admin', '2023-02-24 10:36:59', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (30, 'router.systemMonitoring', '系统监控', 'System Monitoring', 'admin', '2023-02-24 10:36:59', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (31, 'router.generatePages', '生成页面', 'Generate Pages', 'admin', '2023-02-24 10:44:04', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (32, 'router.templateManagement', '模板管理', 'Template Management', 'admin', '2023-02-24 10:44:31', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (33, 'router.templateGroup', '模板分组', 'Template Group', 'admin', '2023-02-24 10:45:10', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (34, 'router.fieldManagement', '字段管理', 'Field Management', 'admin', '2023-02-24 10:46:04', 'admin', '2023-03-07 14:27:48', '0');
INSERT INTO `sys_i18n` VALUES (35, 'router.wechatPlatform', '公众号平台', 'WeChat Platform', 'admin', '2023-02-24 10:48:51', 'admin', '2023-02-24 11:03:41', '0');
INSERT INTO `sys_i18n` VALUES (36, 'router.accountManagement', '账号管理', 'Account Management', 'admin', '2023-02-24 10:13:34', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (37, 'router.menuSettings', '菜单设置', 'Menu Settings', 'admin', '2023-02-24 14:02:22', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (38, 'router.fanManagement', '粉丝管理', 'Fan Management', 'admin', '2023-02-24 14:03:44', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (39, 'router.messageManagement', '消息管理', 'Message Management', 'admin', '2023-02-24 14:03:45', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (40, 'router.paymentSystem', '支付系统', 'Payment System', 'admin', '2023-02-24 14:03:46', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (41, 'router.checkoutCounter', '收银台', 'Checkout Counter', 'admin', '2023-02-24 14:03:47', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (42, 'router.mediaManagement', '素材管理', 'Media Management', 'admin', '2023-02-24 14:03:48', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (43, 'router.paymentChannel', '支付渠道', 'Payment Channel', 'admin', '2023-02-24 14:03:49', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (44, 'router.productOrder', '商品订单', 'Product Order', 'admin', '2023-02-24 14:03:50', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (45, 'router.notificationRecord', '通知记录', 'Notification Record', 'admin', '2023-02-24 14:03:51', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (46, 'router.refundOrder', '退款订单', 'Refund Order', 'admin', '2023-02-24 14:03:52', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (47, 'router.paymentOrder', '支付订单', 'Payment Order', 'admin', '2023-02-24 14:03:53', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (48, 'router.autoReply', '自动回复', 'Auto Reply', 'admin', '2023-02-24 14:03:54', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (49, 'router.operationalData', '运营数据', 'Operational Data', 'admin', '2023-02-24 14:03:55', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (50, 'router.logManagement', '日志管理', 'Log Management', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (51, 'router.collaborativeOffice', '协同办公', 'Collaborative Office', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (32, 'router.templateManagement', '模板管理', 'Templates', 'admin', '2023-02-24 10:44:31', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (33, 'router.templateGroup', '模板分组', 'Template Groups', 'admin', '2023-02-24 10:45:10', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (34, 'router.fieldManagement', '字段管理', 'Fields', 'admin', '2023-02-24 10:46:04', 'admin', '2023-03-07 14:27:48', '0');
INSERT INTO `sys_i18n` VALUES (35, 'router.wechatPlatform', '公众号平台', 'WeChat', 'admin', '2023-02-24 10:48:51', 'admin', '2023-02-24 11:03:41', '0');
INSERT INTO `sys_i18n` VALUES (36, 'router.accountManagement', '账号管理', 'Accounts', 'admin', '2023-02-24 10:13:34', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (37, 'router.menuSettings', '菜单设置', 'Menus', 'admin', '2023-02-24 14:02:22', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (38, 'router.fanManagement', '粉丝管理', 'Followers', 'admin', '2023-02-24 14:03:44', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (39, 'router.messageManagement', '消息管理', 'Messages', 'admin', '2023-02-24 14:03:45', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (40, 'router.paymentSystem', '支付系统', 'Payments', 'admin', '2023-02-24 14:03:46', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (41, 'router.checkoutCounter', '收银台', 'Checkout', 'admin', '2023-02-24 14:03:47', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (42, 'router.mediaManagement', '素材管理', 'Media', 'admin', '2023-02-24 14:03:48', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (43, 'router.paymentChannel', '支付渠道', 'Channels', 'admin', '2023-02-24 14:03:49', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (44, 'router.productOrder', '商品订单', 'Product Orders', 'admin', '2023-02-24 14:03:50', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (45, 'router.notificationRecord', '通知记录', 'Notifications', 'admin', '2023-02-24 14:03:51', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (46, 'router.refundOrder', '退款订单', 'Refunds', 'admin', '2023-02-24 14:03:52', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (47, 'router.paymentOrder', '支付订单', 'Payment Orders', 'admin', '2023-02-24 14:03:53', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (48, 'router.autoReply', '自动回复', 'Auto Replies', 'admin', '2023-02-24 14:03:54', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (49, 'router.operationalData', '运营数据', 'Analytics', 'admin', '2023-02-24 14:03:55', '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (50, 'router.logManagement', '日志管理', 'Logs', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (51, 'router.collaborativeOffice', '协同办公', 'Office', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (52, 'router.modelManagement', '模型管理', 'Model Management', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (53, 'router.modelDiagramView', '模型图查看', 'Model Diagram View', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (54, 'router.processManagement', '流程管理', 'Process Management', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (54, 'router.processManagement', '流程管理', 'Workflows', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (55, 'router.leaveWorkOrder', '请假工单', 'Leave Work Order', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (56, 'router.todoTask', '我的待办', 'Todo Task', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (57, 'router.tagManagement', '标签管理', 'Tag Management', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (58, 'router.articleInformation', '文章资讯', 'Article Information', ' ', '2023-08-10 13:40:09', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (59, 'router.articleCategory', '文章分类', 'Article Category', ' ', '2023-08-10 13:40:48', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (60, 'router.interfaceSettings', '界面设置', 'Interface Settings', ' ', '2023-08-10 13:41:21', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (62, 'router.cacheMonitoring', '缓存监控', 'Cache Monitoring', ' ', '2023-08-10 13:42:35', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (63, 'rotuer. initiateProcess', '发起流程', 'Initiate Process', ' ', '2023-08-10 13:44:23', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (64, 'router.taskManagement', '任务管理', 'Task Management', ' ', '2023-08-10 13:44:53', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (65, 'router.myInitiations', '我的发起', 'My Initiations', ' ', '2023-08-10 13:45:17', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (56, 'router.todoTask', '待办任务', 'Pending Tasks', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (57, 'router.tagManagement', '标签管理', 'Tags', 'admin', NULL, '', NULL, '0');
INSERT INTO `sys_i18n` VALUES (58, 'router.articleInformation', '文章资讯', 'Articles', ' ', '2023-08-10 13:40:09', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (59, 'router.articleCategory', '文章分类', 'Categories', ' ', '2023-08-10 13:40:48', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (60, 'router.interfaceSettings', '界面设置', 'Page Settings', ' ', '2023-08-10 13:41:21', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (62, 'router.cacheMonitoring', '缓存监控', 'Cache Monitor', ' ', '2023-08-10 13:42:35', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (63, 'rotuer. initiateProcess', '发起流程', 'Start Workflow', ' ', '2023-08-10 13:44:23', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (64, 'router.taskManagement', '任务管理', 'Tasks', ' ', '2023-08-10 13:44:53', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (65, 'router.myInitiations', '我的发起', 'My Requests', ' ', '2023-08-10 13:45:17', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (66, 'router.copiedtoMe', '抄送给我', 'Copied to Me', ' ', '2023-08-10 13:45:46', ' ', NULL, '0');
INSERT INTO `sys_i18n` VALUES (67, 'router.completedTasks', '我的已办', 'Completed Tasks', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (68, 'router.bizPlatform', '业务平台', 'Biz Platform', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (69, 'router.baseTools', '基础工具', 'Base Tools', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (70, 'router.route', '路由管理', 'Route Management', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (71, 'router.datav', '大屏看板', 'Data Visual', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (72, 'router.bi', '数据报表', 'Bi Report', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (67, 'router.completedTasks', '我的已办', 'Handled Tasks', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (68, 'router.bizPlatform', '业务平台', 'Business', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (69, 'router.baseTools', '基础工具', 'Tools', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (70, 'router.route', '路由管理', 'Routes', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (71, 'router.datav', '大屏看板', 'Dashboards', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (72, 'router.bi', '数据报表', 'Reports', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (73, 'router.message', '信息推送', 'Message Push', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (74, 'router.sensitiveWords', '敏感词管理', 'Sensitive words', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (74, 'router.sensitiveWords', '敏感词管理', 'Sensitive Words', ' ', '2023-08-10 13:46:37', ' ', '2023-08-10 13:47:09', '0');
INSERT INTO `sys_i18n` VALUES (75, 'router.clarityMonitoring', '站点统计', 'Site Analytics', 'admin', '2026-03-26 00:00:00', 'admin', NULL, '0');
INSERT INTO `sys_i18n` VALUES (128, 'router.siteConfig', '网站配置', 'Site Settings', 'admin', '2026-11-03 00:00:00', 'admin', NULL, '0');
INSERT INTO `sys_i18n` VALUES (129, 'router.sysArea', '行政区划', 'Admin Divisions', 'admin', '2026-11-03 00:00:00', 'admin', NULL, '0');
INSERT INTO `sys_i18n` VALUES (130, 'router.businessForm', '业务表单', 'Business Forms', 'admin', '2026-11-03 00:00:00', 'admin', NULL, '0');
INSERT INTO `sys_i18n` VALUES (131, 'router.databaseTables', '数据表管理', 'Database Tables', 'admin', '2026-11-03 00:00:00', 'admin', NULL, '0');
COMMIT;
-- ----------------------------
@@ -484,12 +486,12 @@ CREATE TABLE `sys_log` (
`log_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '日志类型',
`title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '日志标题',
`service_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '服务ID',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remote_addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '远程地址',
`user_agent` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '用户代理',
`user_agent` text COLLATE utf8mb4_general_ci COMMENT '浏览器标识',
`request_uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '请求URI',
`method` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '请求方法',
`params` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '请求参数',
@@ -526,9 +528,9 @@ CREATE TABLE `sys_menu` (
`keep_alive` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '是否缓存,0否,1是',
`embedded` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '是否内嵌,0否,1是',
`menu_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '菜单类型,0:菜单 1:按钮',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标志,0未删除,1已删除',
`tenant_id` bigint(20) unsigned DEFAULT NULL COMMENT '租户ID',
@@ -686,6 +688,7 @@ INSERT INTO `sys_menu` VALUES (4011,'信息推送查看','sys_message_view',NULL
INSERT INTO `sys_menu` VALUES (4012,'信息推送新增','sys_message_add',NULL,NULL,4010,'1','1',1,'0',NULL,'1','',NULL,'admin','2023-10-25 14:52:00','0',1);
INSERT INTO `sys_menu` VALUES (4013,'信息推送修改','sys_message_edit',NULL,NULL,4010,'1','1',2,'0',NULL,'1','',NULL,'admin','2023-10-25 14:52:04','0',1);
INSERT INTO `sys_menu` VALUES (4014,'信息推送删除','sys_message_del',NULL,NULL,4010,'1','1',3,'0',NULL,'1','',NULL,'admin','2023-10-25 14:52:09','0',1);
INSERT INTO `sys_menu` VALUES (4015, '信息推送导出', 'sys_message_export', NULL, NULL, 4010, NULL, '1', 10, '0', '0', '1', 'admin', NOW(), 'admin', NOW(), '0', 1);
INSERT INTO `sys_menu` VALUES (5000,'支付系统',NULL,'/pay',NULL,9900,'iconfont icon-duoqudaozhifuxitongguanli','1',1,'0','0','0','admin','2023-02-27 10:57:14','admin','2023-11-27 14:52:11','0',1);
INSERT INTO `sys_menu` VALUES (5001,'收银台',NULL,'/biz/pay/cd/index',NULL,5000,'iconfont icon-shouyintai','1',0,'0','0','0','admin','2023-02-27 10:58:13','admin','2023-11-01 17:28:57','0',1);
INSERT INTO `sys_menu` VALUES (5002,'支付渠道',NULL,'/biz/pay/channel/index',NULL,5000,'iconfont icon-zhifuqudao','1',1,'0','0','0','admin','2023-02-27 19:36:55','admin','2023-11-01 17:29:01','0',1);
@@ -759,7 +762,6 @@ INSERT INTO `sys_menu` VALUES (7804,'删除','app_appContacts_del',NULL,NULL,780
INSERT INTO `sys_menu` VALUES (7805,'导入导出','app_appContacts_export',NULL,NULL,7800,NULL,'1',1,'0',NULL,'1','admin','2025-05-29 17:30:31',' ',NULL,'0',1);
INSERT INTO `sys_menu` VALUES (9000,'开发平台',NULL,'/gen',NULL,-1,'iconfont icon-DevOps','1',9,'0','0','0','','2019-08-12 09:35:16','admin','2025-01-26 22:38:09','0',1);
INSERT INTO `sys_menu` VALUES (9005,'数据源管理',NULL,'/gen/datasource/index',NULL,9000,'iconfont icon-shujuyuanguanli','1',0,'0',NULL,'0','','2019-08-12 09:42:11','admin','2025-01-26 22:26:51','0',1);
INSERT INTO `sys_menu` VALUES (9006,'表单设计',NULL,'/gen/design/index',NULL,9000,'iconfont icon-AIshiyanshi','0',2,'0','0','0','','2019-08-16 10:08:56','admin','2023-02-23 14:06:50','0',1);
INSERT INTO `sys_menu` VALUES (9007,'生成页面',NULL,'/gen/gener/index',NULL,9000,'iconfont icon-tongzhi4','0',1,'0','0','0','admin','2023-02-20 09:58:23','admin','2023-02-20 14:41:43','0',1);
INSERT INTO `sys_menu` VALUES (9050,'元数据管理',NULL,'/gen/metadata',NULL,9000,'iconfont icon-yuanshujuguanli','1',9,'0','0','0','','2018-07-27 01:13:21','admin','2023-02-23 19:55:10','0',1);
INSERT INTO `sys_menu` VALUES (9051,'模板管理',NULL,'/gen/template/index',NULL,9050,'iconfont icon-mti-mobanguanli','1',5,'0','0','0','admin','2023-02-21 11:22:54','admin','2023-02-23 19:56:03','0',1);
@@ -806,8 +808,8 @@ CREATE TABLE `sys_oauth_client_details` (
`additional_information` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '附加信息',
`autoapprove` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '自动授权',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记,0未删除,1已删除',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '所属租户ID',
@@ -818,11 +820,11 @@ CREATE TABLE `sys_oauth_client_details` (
-- Records of sys_oauth_client_details
-- ----------------------------
BEGIN;
INSERT INTO `sys_oauth_client_details` VALUES (1, 'app', NULL, 'app', 'server', 'password,refresh_token,authorization_code,client_credentials,mobile', 'http://localhost:4040/sso1/login,http://localhost:4041/sso1/login,http://localhost:8080/renren-admin/sys/oauth2-sso,http://localhost:8090/sys/oauth2-sso', NULL, 43200, 2592001, '{\"enc_flag\":\"1\",\"captcha_flag\":\"1\",\"online_quantity\":\"1\"}', 'true', '0', '', 'admin', NULL, '2023-02-09 13:54:54', 1);
INSERT INTO `sys_oauth_client_details` VALUES (1, 'app', NULL, 'app', 'server', 'password,refresh_token,authorization_code,mobile', 'http://localhost:4040/sso1/login,http://localhost:4041/sso1/login,http://localhost:8080/renren-admin/sys/oauth2-sso,http://localhost:8090/sys/oauth2-sso', NULL, 43200, 2592001, '{\"enc_flag\":\"1\",\"captcha_flag\":\"1\",\"online_quantity\":\"1\"}', 'true', '0', '', 'admin', NULL, '2023-02-09 13:54:54', 1);
INSERT INTO `sys_oauth_client_details` VALUES (2, 'daemon', NULL, 'daemon', 'server', 'password,refresh_token', NULL, NULL, 43200, 2592001, '{\"enc_flag\":\"1\",\"captcha_flag\":\"1\"}', 'true', '0', ' ', ' ', NULL, NULL, 1);
INSERT INTO `sys_oauth_client_details` VALUES (3, 'gen', NULL, 'gen', 'server', 'password,refresh_token', NULL, NULL, 43200, 2592001, '{\"enc_flag\":\"1\",\"captcha_flag\":\"1\"}', 'true', '0', ' ', ' ', NULL, NULL, 1);
INSERT INTO `sys_oauth_client_details` VALUES (4, 'mp', NULL, 'mp', 'server', 'password,refresh_token', NULL, NULL, 43200, 2592001, '{\"enc_flag\":\"1\",\"captcha_flag\":\"1\"}', 'true', '0', ' ', ' ', NULL, NULL, 1);
INSERT INTO `sys_oauth_client_details` VALUES (5, 'pig', NULL, 'pig', 'server', 'password,refresh_token,authorization_code,client_credentials,mobile', 'http://localhost:4040/sso1/login,http://localhost:4041/sso1/login,http://localhost:8080/renren-admin/sys/oauth2-sso,http://localhost:8090/sys/oauth2-sso', NULL, 43200, 2592001, '{\"enc_flag\":\"1\",\"captcha_flag\":\"1\",\"online_quantity\":\"1\"}', 'false', '0', '', 'admin', NULL, '2023-03-08 11:32:41', 1);
INSERT INTO `sys_oauth_client_details` VALUES (5, 'pig', NULL, 'pig', 'server', 'password,refresh_token,authorization_code,mobile', 'http://localhost:4040/sso1/login,http://localhost:4041/sso1/login,http://localhost:8080/renren-admin/sys/oauth2-sso,http://localhost:8090/sys/oauth2-sso', NULL, 43200, 2592001, '{\"enc_flag\":\"1\",\"captcha_flag\":\"1\",\"online_quantity\":\"1\"}', 'false', '0', '', 'admin', NULL, '2023-03-08 11:32:41', 1);
INSERT INTO `sys_oauth_client_details` VALUES (6, 'test', NULL, 'test', 'server', 'password,refresh_token', NULL, NULL, 43200, 2592001, '{ \"enc_flag\":\"1\",\"captcha_flag\":\"0\"}', 'true', '0', ' ', ' ', NULL, NULL, 1);
INSERT INTO `sys_oauth_client_details` VALUES (7, 'social', NULL, 'social', 'server', 'password,refresh_token,mobile', NULL, NULL, 43200, 2592001, '{ \"enc_flag\":\"0\",\"captcha_flag\":\"0\"}', 'true', '0', ' ', ' ', NULL, NULL, 1);
INSERT INTO `sys_oauth_client_details` VALUES (8, 'mini', NULL, 'mini', 'server', 'password,mobile', NULL, NULL, 160000000, 160000000, '{\"captcha_flag\":\"0\",\"enc_flag\":\"0\",\"online_quantity\":\"1\"}', 'true', '0', 'admin', 'admin', '2023-01-29 16:38:06', '2023-01-29 17:21:56', 1);
@@ -865,8 +867,8 @@ CREATE TABLE `sys_public_param` (
`public_value` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '值',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '状态,0禁用,1启用',
`validate_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '校验码',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`public_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '类型,0未知,1系统,2业务',
@@ -895,8 +897,8 @@ INSERT INTO `sys_public_param` VALUES (12, '强制重置密码', 'SITE_FORCE_RES
INSERT INTO `sys_public_param` VALUES (13, '强制退出', 'SITE_FORCE_LOGOUT', '0', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (14, '短信登录开关', 'SITE_SMS_LOGIN_ENABLE', '1', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (15, '社交登录开关', 'SITE_SOCIAL_LOGIN_ENABLE', '1', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (16, '前端注册开关', 'SITE_REGISTER_ENABLE', '1', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (17, '找回/重置密码开关', 'SITE_RESET_PASSWORD', '1', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (16, '前端注册开关', 'SITE_REGISTER_ENABLE', '0', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (17, '找回/重置密码开关', 'SITE_RESET_PASSWORD', '0', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (18, '国际化切换开关', 'SITE_I18N_ENABLE', '1', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (19, '亮暗模式切换开关', 'SITE_DARK_MODE_ENABLE', '1', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
INSERT INTO `sys_public_param` VALUES (20, '反调试保护', 'SITE_ANTI_DEBUG_ENABLE', '0', '0', NULL, 'admin', 'admin', '2026-03-28 00:00:00', NULL, '2', '1', '0', 1);
@@ -925,8 +927,8 @@ CREATE TABLE `sys_role` (
`role_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '角色描述',
`ds_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '2' COMMENT '数据权限类型,0全部,1自定义,2本部门及以下,3本部门,4仅本人',
`ds_scope` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '数据权限范围',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记,0未删除,1已删除',
@@ -1107,6 +1109,7 @@ INSERT INTO `sys_role_menu` VALUES (1, 4011);
INSERT INTO `sys_role_menu` VALUES (1, 4012);
INSERT INTO `sys_role_menu` VALUES (1, 4013);
INSERT INTO `sys_role_menu` VALUES (1, 4014);
INSERT INTO `sys_role_menu` VALUES (1, 4015);
INSERT INTO `sys_role_menu` VALUES (1, 5000);
INSERT INTO `sys_role_menu` VALUES (1, 5001);
INSERT INTO `sys_role_menu` VALUES (1, 5002);
@@ -1180,7 +1183,6 @@ INSERT INTO `sys_role_menu` VALUES (1, 7804);
INSERT INTO `sys_role_menu` VALUES (1, 7805);
INSERT INTO `sys_role_menu` VALUES (1, 9000);
INSERT INTO `sys_role_menu` VALUES (1, 9005);
INSERT INTO `sys_role_menu` VALUES (1, 9006);
INSERT INTO `sys_role_menu` VALUES (1, 9007);
INSERT INTO `sys_role_menu` VALUES (1, 9050);
INSERT INTO `sys_role_menu` VALUES (1, 9051);
@@ -1234,18 +1236,18 @@ CREATE TABLE `sys_role_widget` (
DROP TABLE IF EXISTS `sys_route_conf`;
CREATE TABLE `sys_route_conf` (
`id` bigint(20) NOT NULL COMMENT '主键',
`route_name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`route_id` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`route_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`route_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`predicates` json DEFAULT NULL COMMENT '断言',
`filters` json DEFAULT NULL COMMENT '过滤器',
`uri` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`uri` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`sort_order` int(11) DEFAULT '0' COMMENT '排序',
`metadata` json DEFAULT NULL COMMENT '路由元信息',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`del_flag` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '0',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='路由配置表';
@@ -1281,9 +1283,9 @@ CREATE TABLE `sys_schedule` (
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT '内容',
`schedule_time` time DEFAULT NULL COMMENT '时间',
`schedule_date` date DEFAULT NULL COMMENT '日期',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记',
`tenant_id` bigint(20) unsigned DEFAULT NULL COMMENT '租户ID',
@@ -1308,8 +1310,8 @@ CREATE TABLE `sys_social_details` (
`app_secret` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '应用密钥',
`redirect_url` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '回调地址',
`ext` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '拓展字段',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记,0未删除,1已删除',
@@ -1398,22 +1400,15 @@ CREATE TABLE `sys_user` (
`nickname` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '昵称',
`name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '姓名',
`email` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '邮箱地址',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`lock_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '锁定标记,0未锁定,9已锁定',
`password_expire_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '密码是否过期,0未过期,9已过期',
`password_modify_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记,0未删除,1已删除',
`wx_openid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信登录openId',
`mini_openid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '小程序openId',
`qq_openid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'QQ openId',
`gitee_login` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '码云标识',
`osc_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '开源中国标识',
`wx_cp_userid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '企业微信唯一ID',
`wx_ding_userid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '钉钉唯一ID',
`dept_id` bigint(20) DEFAULT NULL COMMENT '主部门ID',
`dept_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '主部门ID',
`tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '所属租户ID',
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户表';
@@ -1422,9 +1417,24 @@ CREATE TABLE `sys_user` (
-- Records of sys_user
-- ----------------------------
BEGIN;
INSERT INTO `sys_user` VALUES (1, 'admin', '$2a$10$c/Ae0pRjJtMZg3BnvVpO.eIK6WYWVbKTzqgdy3afR7w.vd.xi3Mgy', '', '17338122125', '/admin/sys-file/local/2a14ae08150e483c93e12ac8934173e2.png', '管理员666777', '管理员', 'sw@mail.pigxl.vip', ' ', 'admin', '2018-04-20 07:15:18', '2023-04-03 14:00:06', '0', '0', NULL, '0', NULL, 'oBxPy5E-v82xWGsfzZVzkD3wEX64', NULL, 'log4j', NULL, NULL,NULL,1,1);
INSERT INTO `sys_user` (`user_id`, `username`, `password`, `salt`, `phone`, `avatar`, `nickname`, `name`, `email`, `create_by`, `update_by`, `create_time`, `update_time`, `lock_flag`, `password_expire_flag`, `password_modify_time`, `del_flag`, `dept_id`, `tenant_id`) VALUES (1, 'admin', '$2a$10$c/Ae0pRjJtMZg3BnvVpO.eIK6WYWVbKTzqgdy3afR7w.vd.xi3Mgy', '', '17338122125', '/admin/sys-file/local/2a14ae08150e483c93e12ac8934173e2.png', '管理员666777', '管理员', 'sw@mail.pigxl.vip', ' ', 'admin', '2018-04-20 07:15:18', '2023-04-03 14:00:06', '0', '0', NULL, '0', 1, 1);
COMMIT;
-- ----------------------------
-- Table structure for sys_user_social
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_social`;
CREATE TABLE `sys_user_social` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '社交平台类型',
`identifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '唯一凭据',
`create_time` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_social_identifier` (`type`,`identifier`) USING BTREE,
UNIQUE KEY `uk_user_social_type` (`user_id`,`type`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户社交登录身份表';
-- ----------------------------
-- Table structure for sys_user_post
-- ----------------------------
@@ -1480,8 +1490,12 @@ CREATE TABLE `sys_message` (
`update_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '删除时间',
`tenant_id` bigint DEFAULT NULL COMMENT '租户',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='站内信';
`business_type` varchar(32) DEFAULT NULL COMMENT '业务类型',
`business_id` varchar(64) DEFAULT NULL COMMENT '业务记录ID',
`request_key` varchar(100) DEFAULT NULL COMMENT '调用方事件标识',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_message_request` (`tenant_id`,`request_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='站内信息';
-- ----------------------------
-- Records of sys_message
@@ -1505,7 +1519,8 @@ CREATE TABLE `sys_message_relation` (
`update_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '更新人',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '删除时间',
`tenant_id` bigint DEFAULT NULL COMMENT '租户',
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `idx_relation_msg_user` (`msg_id`,`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统消息推送记录';
DROP TABLE IF EXISTS `sys_system_config`;
+67 -8
View File
@@ -1,3 +1,4 @@
-- 本文件仅用于初始化,不用于升级已有数据库。
USE pigxx_flow;
SET NAMES utf8mb4;
@@ -137,11 +138,13 @@ CREATE TABLE `process_node_data` (
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标志',
`create_time` datetime COMMENT '创建时间',
`update_time` datetime COMMENT '更新时间',
`process_definition_id` varchar(64) DEFAULT NULL COMMENT '引擎流程定义ID,发布版本快照',
`flow_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '流程id',
`data` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '表单数据',
`node_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '所属租户id',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_node_definition` (`tenant_id`,`process_definition_id`,`node_id`),
KEY `data_idx_id` (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1195 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='流程节点数据';
@@ -202,9 +205,21 @@ CREATE TABLE `process_node_record_assign_user` (
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_node_record_user` (`tenant_id`,`node_record_id`,`user_id`) USING BTREE,
KEY `user_idx_id` (`id`) USING BTREE,
KEY `idx_add_sign_record_id` (`add_sign_record_id`) USING BTREE
KEY `idx_add_sign_record_id` (`add_sign_record_id`) USING BTREE,
KEY `idx_assign_tenant_instance_del` (`tenant_id`,`process_instance_id`,`del_flag`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=597 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='流程节点记录-执行人';
-- Flowable 列表查询索引:act_* 表由引擎初始化,不在本脚本中创建。
-- 首次启动完成后,在业务库手动执行以下 SQL 一次;已有同名索引时不要重复执行。
-- 待办:按审批人筛选、创建时间排序。
-- ALTER TABLE `act_ru_task`
-- ADD INDEX `idx_task_assignee_create` (`ASSIGNEE_`, `CREATE_TIME_`),
-- ALGORITHM=INPLACE, LOCK=NONE;
-- 已办:按审批人及结束时间查询、统计。
-- ALTER TABLE `act_hi_actinst`
-- ADD INDEX `idx_act_assignee_end` (`ASSIGNEE_`, `END_TIME_`),
-- ALGORITHM=INPLACE, LOCK=NONE;
-- ----------------------------
-- Table structure for process_task_add_sign_record
@@ -255,21 +270,65 @@ CREATE TABLE `process_starter` (
DROP TABLE IF EXISTS `bmp_oa_leave`;
CREATE TABLE `bmp_oa_leave` (
`id` bigint NOT NULL COMMENT '请假表单主键',
`username` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '申请人',
`username` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '申请人',
`leave_type` int NOT NULL COMMENT '请假类型',
`leave_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '请假原因',
`leave_reason` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '请假原因',
`start_time` datetime NOT NULL COMMENT '开始时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`leave_day` int NOT NULL COMMENT '请假天数',
`leave_status` int DEFAULT NULL COMMENT '请假结果',
`process_instance_id` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '流程实例的编号',
`create_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
`process_instance_id` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '流程实例的编号',
`create_by` varchar(64) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者',
`create_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
`update_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',
`update_by` varchar(64) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者',
`update_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间',
`del_flag` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '是否删除',
`del_flag` char(1) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='请假表单';
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='请假表单';
-- ----------------------------
-- Table structure for flow_draft
-- ----------------------------
DROP TABLE IF EXISTS `flow_draft`;
CREATE TABLE `flow_draft` (
`id` varchar(36) COLLATE utf8mb4_general_ci NOT NULL COMMENT '草稿ID',
`tenant_id` bigint NOT NULL COMMENT '所属租户ID',
`user_id` bigint NOT NULL COMMENT '草稿所属用户ID',
`kind` varchar(16) COLLATE utf8mb4_general_ci NOT NULL COMMENT '草稿类型:DESIGN 流程设计,APPLICATION 申请',
`flow_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '关联流程ID,未建立流程时为空串',
`title` varchar(200) COLLATE utf8mb4_general_ci NOT NULL COMMENT '草稿标题',
`payload` longtext COLLATE utf8mb4_general_ci NOT NULL COMMENT '草稿内容 JSON,最多 1 MB',
`revision` bigint NOT NULL COMMENT '乐观锁版本号',
`updated_at` timestamp(3) NOT NULL COMMENT '最后保存时间',
PRIMARY KEY (`id`),
KEY `idx_draft_owner` (`tenant_id`,`user_id`,`kind`,`updated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='个人流程草稿';
-- ----------------------------
-- Table structure for flow_notice
-- ----------------------------
DROP TABLE IF EXISTS `flow_notice`;
CREATE TABLE `flow_notice` (
`id` varchar(36) COLLATE utf8mb4_general_ci NOT NULL COMMENT '通知事件ID',
`tenant_id` bigint NOT NULL COMMENT '所属租户ID',
`user_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '接收人ID,超时提醒到期前为空串',
`event_key` varchar(180) COLLATE utf8mb4_general_ci NOT NULL COMMENT '事件去重键',
`kind` varchar(16) COLLATE utf8mb4_general_ci NOT NULL COMMENT '通知类型:TODO 待办,URGE 催办,OVERDUE 超时提醒',
`title` varchar(200) COLLATE utf8mb4_general_ci NOT NULL COMMENT '通知标题',
`task_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '引擎任务ID',
`instance_id` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '引擎流程实例ID',
`created_at` timestamp(3) NOT NULL COMMENT '创建时间',
`status` varchar(16) COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'SENT' COMMENT '状态:PENDING 待到期,READY 待投递,SENT 已投递,REMOVING 待撤销,CANCELLED 已取消,DISABLED 已停用',
`scheduled_at` timestamp(3) NULL DEFAULT NULL COMMENT '计划投递或到期时间',
`sent_at` timestamp(3) NULL DEFAULT NULL COMMENT '投递到统一站内信的时间',
`reminder_key` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '超时提醒任务ID,每个任务最多一条',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_notice_event` (`tenant_id`,`user_id`,`event_key`),
UNIQUE KEY `uk_notice_reminder` (`tenant_id`,`reminder_key`),
KEY `idx_notice_owner` (`tenant_id`,`user_id`,`created_at`),
KEY `idx_notice_due` (`status`,`scheduled_at`),
KEY `idx_notice_task` (`tenant_id`,`task_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='流程通知事件';
SET FOREIGN_KEY_CHECKS = 1;
+70 -70
View File
@@ -8,9 +8,9 @@ SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_blob_triggers`;
CREATE TABLE `qrtz_blob_triggers` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`blob_data` blob,
PRIMARY KEY (`sched_name`,`trigger_name`,`trigger_group`) USING BTREE,
CONSTRAINT `qrtz_blob_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
@@ -27,8 +27,8 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_calendars`;
CREATE TABLE `qrtz_calendars` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`calendar_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`calendar_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`calendar` blob NOT NULL,
PRIMARY KEY (`sched_name`,`calendar_name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -44,11 +44,11 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_cron_triggers`;
CREATE TABLE `qrtz_cron_triggers` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`cron_expression` varchar(200) CHARACTER SET utf8 NOT NULL,
`time_zone_id` varchar(80) CHARACTER SET utf8 DEFAULT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`cron_expression` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`time_zone_id` varchar(80) CHARACTER SET utf8mb4 DEFAULT NULL,
PRIMARY KEY (`sched_name`,`trigger_name`,`trigger_group`) USING BTREE,
CONSTRAINT `qrtz_cron_triggers_ibfk_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -64,19 +64,19 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_fired_triggers`;
CREATE TABLE `qrtz_fired_triggers` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`entry_id` varchar(95) CHARACTER SET utf8 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`instance_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`entry_id` varchar(95) CHARACTER SET utf8mb4 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`instance_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`fired_time` bigint NOT NULL,
`sched_time` bigint NOT NULL,
`priority` int NOT NULL,
`state` varchar(16) CHARACTER SET utf8 NOT NULL,
`job_name` varchar(200) CHARACTER SET utf8 DEFAULT NULL,
`job_group` varchar(200) CHARACTER SET utf8 DEFAULT NULL,
`is_nonconcurrent` varchar(1) CHARACTER SET utf8 DEFAULT NULL,
`requests_recovery` varchar(1) CHARACTER SET utf8 DEFAULT NULL,
`state` varchar(16) CHARACTER SET utf8mb4 NOT NULL,
`job_name` varchar(200) CHARACTER SET utf8mb4 DEFAULT NULL,
`job_group` varchar(200) CHARACTER SET utf8mb4 DEFAULT NULL,
`is_nonconcurrent` varchar(1) CHARACTER SET utf8mb4 DEFAULT NULL,
`requests_recovery` varchar(1) CHARACTER SET utf8mb4 DEFAULT NULL,
PRIMARY KEY (`sched_name`,`entry_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -91,15 +91,15 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_job_details`;
CREATE TABLE `qrtz_job_details` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`job_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`job_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`description` varchar(250) CHARACTER SET utf8 DEFAULT NULL,
`job_class_name` varchar(250) CHARACTER SET utf8 NOT NULL,
`is_durable` varchar(1) CHARACTER SET utf8 NOT NULL,
`is_nonconcurrent` varchar(1) CHARACTER SET utf8 NOT NULL,
`is_update_data` varchar(1) CHARACTER SET utf8 NOT NULL,
`requests_recovery` varchar(1) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`job_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`job_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`description` varchar(250) CHARACTER SET utf8mb4 DEFAULT NULL,
`job_class_name` varchar(250) CHARACTER SET utf8mb4 NOT NULL,
`is_durable` varchar(1) CHARACTER SET utf8mb4 NOT NULL,
`is_nonconcurrent` varchar(1) CHARACTER SET utf8mb4 NOT NULL,
`is_update_data` varchar(1) CHARACTER SET utf8mb4 NOT NULL,
`requests_recovery` varchar(1) CHARACTER SET utf8mb4 NOT NULL,
`job_data` blob,
PRIMARY KEY (`sched_name`,`job_name`,`job_group`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -115,8 +115,8 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_locks`;
CREATE TABLE `qrtz_locks` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`lock_name` varchar(40) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`lock_name` varchar(40) CHARACTER SET utf8mb4 NOT NULL,
PRIMARY KEY (`sched_name`,`lock_name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -131,8 +131,8 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_paused_trigger_grps`;
CREATE TABLE `qrtz_paused_trigger_grps` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
PRIMARY KEY (`sched_name`,`trigger_group`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -147,8 +147,8 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_scheduler_state`;
CREATE TABLE `qrtz_scheduler_state` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`instance_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`instance_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`last_checkin_time` bigint NOT NULL,
`checkin_interval` bigint NOT NULL,
PRIMARY KEY (`sched_name`,`instance_name`) USING BTREE
@@ -165,9 +165,9 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_simple_triggers`;
CREATE TABLE `qrtz_simple_triggers` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`repeat_count` bigint NOT NULL,
`repeat_interval` bigint NOT NULL,
`times_triggered` bigint NOT NULL,
@@ -186,20 +186,20 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_simprop_triggers`;
CREATE TABLE `qrtz_simprop_triggers` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`str_prop_1` varchar(512) CHARACTER SET utf8 DEFAULT NULL,
`str_prop_2` varchar(512) CHARACTER SET utf8 DEFAULT NULL,
`str_prop_3` varchar(512) CHARACTER SET utf8 DEFAULT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`str_prop_1` varchar(512) CHARACTER SET utf8mb4 DEFAULT NULL,
`str_prop_2` varchar(512) CHARACTER SET utf8mb4 DEFAULT NULL,
`str_prop_3` varchar(512) CHARACTER SET utf8mb4 DEFAULT NULL,
`int_prop_1` int DEFAULT NULL,
`int_prop_2` int DEFAULT NULL,
`long_prop_1` bigint DEFAULT NULL,
`long_prop_2` bigint DEFAULT NULL,
`dec_prop_1` decimal(13,4) DEFAULT NULL,
`dec_prop_2` decimal(13,4) DEFAULT NULL,
`bool_prop_1` varchar(1) CHARACTER SET utf8 DEFAULT NULL,
`bool_prop_2` varchar(1) CHARACTER SET utf8 DEFAULT NULL,
`bool_prop_1` varchar(1) CHARACTER SET utf8mb4 DEFAULT NULL,
`bool_prop_2` varchar(1) CHARACTER SET utf8mb4 DEFAULT NULL,
PRIMARY KEY (`sched_name`,`trigger_name`,`trigger_group`) USING BTREE,
CONSTRAINT `QRTZ_SIMPROP_TRIGGERS_IBFK_1` FOREIGN KEY (`sched_name`, `trigger_name`, `trigger_group`) REFERENCES `qrtz_triggers` (`sched_name`, `trigger_name`, `trigger_group`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
@@ -215,20 +215,20 @@ COMMIT;
-- ----------------------------
DROP TABLE IF EXISTS `qrtz_triggers`;
CREATE TABLE `qrtz_triggers` (
`sched_name` varchar(120) CHARACTER SET utf8 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`job_name` varchar(200) CHARACTER SET utf8 NOT NULL,
`job_group` varchar(200) CHARACTER SET utf8 NOT NULL,
`description` varchar(250) CHARACTER SET utf8 DEFAULT NULL,
`sched_name` varchar(120) CHARACTER SET utf8mb4 NOT NULL,
`trigger_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`trigger_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`job_name` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`job_group` varchar(200) CHARACTER SET utf8mb4 NOT NULL,
`description` varchar(250) CHARACTER SET utf8mb4 DEFAULT NULL,
`next_fire_time` bigint DEFAULT NULL,
`prev_fire_time` bigint DEFAULT NULL,
`priority` int DEFAULT NULL,
`trigger_state` varchar(16) CHARACTER SET utf8 NOT NULL,
`trigger_type` varchar(8) CHARACTER SET utf8 NOT NULL,
`trigger_state` varchar(16) CHARACTER SET utf8mb4 NOT NULL,
`trigger_type` varchar(8) CHARACTER SET utf8mb4 NOT NULL,
`start_time` bigint NOT NULL,
`end_time` bigint DEFAULT NULL,
`calendar_name` varchar(200) CHARACTER SET utf8 DEFAULT NULL,
`calendar_name` varchar(200) CHARACTER SET utf8mb4 DEFAULT NULL,
`misfire_instr` smallint DEFAULT NULL,
`job_data` blob,
PRIMARY KEY (`sched_name`,`trigger_name`,`trigger_group`) USING BTREE,
@@ -286,22 +286,22 @@ DROP TABLE IF EXISTS `sys_job_log`;
CREATE TABLE `sys_job_log` (
`job_log_id` bigint NOT NULL COMMENT '任务日志ID',
`job_id` bigint NOT NULL COMMENT '任务id',
`job_name` varchar(64) CHARACTER SET utf8 DEFAULT NULL COMMENT '任务名称',
`job_group` varchar(64) CHARACTER SET utf8 DEFAULT NULL COMMENT '任务组名',
`job_order` char(1) CHARACTER SET utf8 DEFAULT NULL COMMENT '组内执行顺利,值越大执行优先级越高,最大值9,最小值1',
`job_type` char(1) CHARACTER SET utf8 NOT NULL DEFAULT '1' COMMENT '1、java类;2、spring bean名称;3、rest调用;4、jar调用;9其他',
`execute_path` varchar(500) CHARACTER SET utf8 DEFAULT NULL COMMENT 'job_type=3时,rest调用地址,仅支持post协议;job_type=4时,jar路径;其它值为空',
`class_name` varchar(500) CHARACTER SET utf8 DEFAULT NULL COMMENT 'job_type=1时,类完整路径;job_type=2时,spring bean名称;其它值为空',
`method_name` varchar(500) CHARACTER SET utf8 DEFAULT NULL COMMENT '任务方法',
`method_params_value` varchar(2000) CHARACTER SET utf8 DEFAULT NULL COMMENT '参数值',
`cron_expression` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT 'cron执行表达式',
`job_message` varchar(500) CHARACTER SET utf8 DEFAULT NULL COMMENT '日志信息',
`job_log_status` char(1) CHARACTER SET utf8 DEFAULT '0' COMMENT '执行状态(0正常 1失败)',
`execute_time` varchar(30) CHARACTER SET utf8 DEFAULT NULL COMMENT '执行时间',
`exception_info` varchar(2000) CHARACTER SET utf8 DEFAULT '' COMMENT '异常信息',
`job_name` varchar(64) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '任务名称',
`job_group` varchar(64) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '任务组名',
`job_order` char(1) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '组内执行顺利,值越大执行优先级越高,最大值9,最小值1',
`job_type` char(1) CHARACTER SET utf8mb4 NOT NULL DEFAULT '1' COMMENT '1、java类;2、spring bean名称;3、rest调用;4、jar调用;9其他',
`execute_path` varchar(500) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'job_type=3时,rest调用地址,仅支持post协议;job_type=4时,jar路径;其它值为空',
`class_name` varchar(500) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'job_type=1时,类完整路径;job_type=2时,spring bean名称;其它值为空',
`method_name` varchar(500) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '任务方法',
`method_params_value` varchar(2000) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '参数值',
`cron_expression` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'cron执行表达式',
`job_message` varchar(500) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '日志信息',
`job_log_status` char(1) CHARACTER SET utf8mb4 DEFAULT '0' COMMENT '执行状态(0正常 1失败)',
`execute_time` varchar(30) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '执行时间',
`exception_info` varchar(2000) CHARACTER SET utf8mb4 DEFAULT '' COMMENT '异常信息',
`scheduled_fire_time` datetime DEFAULT NULL COMMENT '计划触发时间',
`fire_instance_id` varchar(128) CHARACTER SET utf8 DEFAULT NULL COMMENT 'Quartz触发实例ID',
`dedup_status` char(1) CHARACTER SET utf8 DEFAULT '0' COMMENT '去重状态(0正常执行 1同一触发点重复 2任务运行中跳过)',
`fire_instance_id` varchar(128) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'Quartz触发实例ID',
`dedup_status` char(1) CHARACTER SET utf8mb4 DEFAULT '0' COMMENT '去重状态(0正常执行 1同一触发点重复 2任务运行中跳过)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`tenant_id` bigint NOT NULL DEFAULT '1' COMMENT '租户id',
PRIMARY KEY (`job_log_id`) USING BTREE
+6 -6
View File
@@ -113,8 +113,8 @@ CREATE TABLE `wx_auto_reply` (
`rep_thumb_url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '缩略图url',
`content` json DEFAULT NULL COMMENT '图文消息的内容',
`app_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '公众号ID',
`remark` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注',
`del_flag` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '0' COMMENT '逻辑删除标记(0:显示;1:隐藏)',
`remark` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '逻辑删除标记(0:显示;1:隐藏)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`tenant_id` bigint(20) DEFAULT NULL COMMENT '租户ID',
@@ -159,7 +159,7 @@ CREATE TABLE `wx_msg` (
`id` bigint(20) NOT NULL COMMENT '主键',
`app_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '公众号名称',
`app_logo` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '公众号logo',
`wx_user_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '微信用户ID',
`wx_user_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '微信用户ID',
`nick_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信用户昵称',
`headimg_url` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信用户头像',
`type` char(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '消息分类(1、用户发给公众号;2、公众号发给用户;)',
@@ -177,11 +177,11 @@ CREATE TABLE `wx_msg` (
`rep_location_x` double DEFAULT NULL COMMENT '地理位置维度',
`rep_location_y` double DEFAULT NULL COMMENT '地理位置经度',
`rep_scale` double DEFAULT NULL COMMENT '地图缩放大小',
`read_flag` char(2) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '1' COMMENT '已读标记(1:是;0:否)',
`read_flag` char(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '1' COMMENT '已读标记(1:是;0:否)',
`app_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '公众号ID',
`open_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信唯一标识',
`remark` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '备注',
`del_flag` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '0' COMMENT '逻辑删除标记(0:显示;1:隐藏)',
`remark` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '逻辑删除标记(0:显示;1:隐藏)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`tenant_id` bigint(20) DEFAULT NULL COMMENT '租户ID',
+4 -4
View File
@@ -40,17 +40,17 @@ VALUES (1, 'application-dev.yml', 'DEFAULT_GROUP',
'0dc3faa0d9871245282f6b7d4f2249c0', '2022-12-16 10:44:25', '2023-08-14 11:44:19', 'nacos', '127.0.0.1', '',
'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (2, 'pigx-auth-dev.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n freemarker:\n allow-request-override: false\n allow-session-override: false\n cache: true\n charset: UTF-8\n check-template-location: true\n content-type: text/html\n enabled: true\n expose-request-attributes: false\n expose-session-attributes: false\n expose-spring-macro-helpers: true\n prefer-file-system-access: true\n suffix: .ftl\n template-loader-path: classpath:/templates/\n\n# 前端登录验证码\nsecurity:\n encodeKey: \'pigxpigxpigxpigx\'\n# 验证码相关配置参考: http://t.cn/A647jEdu\naj:\n captcha:\n cache-type: redis\n water-mark: pig4cloud', 'bb6bd9b9e21afdec3d6c88636387df96', '2022-12-16 10:44:25', '2024-04-20 22:47:21', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (3, 'pigx-codegen-dev.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n type: com.alibaba.druid.pool.DruidDataSource\n druid:\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: ${MYSQL_USER:root}\n password: ${MYSQL_PWD:root}\n url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx_codegen}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true\n resources:\n static-locations: classpath:/static/,classpath:/views/\n', 'ed6de06e34e2634c28a24e8a1db99b06', '2022-12-16 10:44:25', '2024-02-22 15:08:09', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (3, 'pigx-codegen-dev.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n type: com.alibaba.druid.pool.DruidDataSource\n druid:\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: ${MYSQL_USER:root}\n password: ${MYSQL_PWD:root}\n url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx_codegen}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true\n', 'e9ca4622d920568cb5daf08145b63b1e', '2022-12-16 10:44:25', '2024-02-22 15:08:09', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (4, 'pigx-daemon-elastic-job-dev.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n type: com.alibaba.druid.pool.DruidDataSource\n druid:\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: ${MYSQL_USER:root}\n password: ${MYSQL_PWD:root}\n url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx_job}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true\n\n# XXL-Job 配置\nxxl:\n job:\n accessToken: default_token', 'ac1b92ceacc451eac15942131e04c6dd', '2022-12-16 10:44:25', '2023-04-07 14:14:50', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (5, 'pigx-gateway-dev.yml', 'DEFAULT_GROUP', '# 固定路由转发配置 无修改\nspring:\n cloud:\n gateway:\n server:\n webflux:\n routes:\n - id: openapi\n uri: lb://pigx-gateway\n predicates:\n - Path=/v3/api-docs/**\n filters:\n - RewritePath=/v3/api-docs/(?<path>.*), /$\\{path}/$\\{path}/v3/api-docs\n\n# gateway 刷新端点\nmanagement:\n endpoint:\n gateway:\n access: unrestricted\n', 'd9454d877ff3d8bd3fd0d5be99810195', '2022-12-16 10:44:25', '2024-04-20 22:47:02', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (6, 'pigx-monitor-dev.yml', 'DEFAULT_GROUP', 'spring:\n # 安全配置\n security:\n user:\n name: ENC(rZHA4LW5hHmhLAAzJoFNag==) # pigx\n password: ENC(bjeyh+Aeii3kHXkoo00ZUw==) # pigx\n autoconfigure:\n exclude: com.pig4cloud.pigx.common.core.config.JacksonConfiguration\n boot:\n admin:\n ui:\n title: \'pigx 服务状态监控\'\n brand: \'pigx 服务状态监控\'\n external-views:\n - label: \"SQL监控\"\n url: /druid/sql.html\n order: 2000\n iframe: true\nmanagement:\n endpoints:\n web:\n exposure:\n include: \'*\'\n endpoint:\n health:\n show-details: ALWAYS #显示详细信息\n\n\n# druid 监控的服务\nmonitor:\n applications:\n - pigx-upms-biz\n', '2bb39e4dee3f90d4186d1e42aa666d2a', '2022-12-16 10:44:25', '2023-04-07 14:12:09', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (7, 'pigx-upms-biz-dev.yml', 'DEFAULT_GROUP', '## spring security 配置\nsecurity:\n oauth2:\n client:\n ignore-urls:\n - /druid/**\n\n# 数据源\nspring:\n datasource:\n type: com.alibaba.druid.pool.DruidDataSource\n druid:\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: ${MYSQL_USER:root}\n password: ${MYSQL_PWD:root}\n url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true\n stat-view-servlet:\n enabled: true\n allow: \"\"\n url-pattern: /druid/*\n #login-username: admin\n #login-password: admin\n filter:\n stat:\n enabled: true\n log-slow-sql: true\n slow-sql-millis: 10000\n merge-sql: false\n wall:\n config:\n multi-statement-allow: true\n\n# 本地文件系统\nfile:\n type: local\n local:\n basePath: /Users/lengleng/Downloads/files', '31570b7a880625228335b7f7e5aaa312', '2022-12-16 10:44:25', '2024-02-22 15:08:36', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (8, 'pigx-daemon-quartz-dev.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n type: com.alibaba.druid.pool.DruidDataSource\n druid:\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: ${MYSQL_USER:root}\n password: ${MYSQL_PWD:root}\n url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx_job}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true\n resources:\n static-locations: classpath:/static/,classpath:/views/', '79619124a30cdb6e8bc1046aaa18d91c', '2022-12-16 10:44:25', '2024-04-19 21:49:00', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (8, 'pigx-daemon-quartz-dev.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n type: com.alibaba.druid.pool.DruidDataSource\n druid:\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: ${MYSQL_USER:root}\n password: ${MYSQL_PWD:root}\n url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx_job}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true\n', 'caa1a7ea301239a18e9a43699e43707e', '2022-12-16 10:44:25', '2024-04-19 21:49:00', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (9, 'pigx-pay-platform-dev.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n type: com.alibaba.druid.pool.DruidDataSource\n druid:\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: ${MYSQL_USER:root}\n password: ${MYSQL_PWD:root}\n url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx_pay}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true\n freemarker:\n allow-request-override: false\n allow-session-override: false\n cache: true\n charset: UTF-8\n check-template-location: true\n content-type: text/html\n enabled: true\n expose-request-attributes: false\n expose-session-attributes: false\n expose-spring-macro-helpers: true\n prefer-file-system-access: true\n suffix: .ftl\n template-loader-path: classpath:/templates/\n# 租户表维护\npigx:\n pay:\n test: lengleng\n aliPayConfig:\n expire-time: 30\n return-url: http://pig4cloud.com\n notify-url: http://payx.yunjihuitong.com/pay/notify/ali/callbak\n wxPayConfig:\n notify-url: https://admin.pig4cloud.com/pay/notify/wx/callbak\n mergePayConfig:\n return-url: http://pig4cloud.com\n notify-url: http://wechat.pigx.top/pay/notify/merge/callbak\n xsequence: #发号器相关配置\n db:\n retry-times: 3\n table-name: pay_sequence', 'f4e5734a99f3c1f059071deac081b33d', '2022-12-16 10:44:25', '2024-02-22 15:09:08', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (11, 'pigx-mp-platform-dev.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n type: com.alibaba.druid.pool.DruidDataSource\n druid:\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: ${MYSQL_USER:root}\n password: ${MYSQL_PWD:root}\n url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx_mp}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true\n resources:\n static-locations: classpath:/static/,classpath:/views/', 'd5936e0c352fb1fbe1d05f054f14c98a', '2022-12-16 10:44:25', '2024-02-22 15:09:20', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (11, 'pigx-mp-platform-dev.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n type: com.alibaba.druid.pool.DruidDataSource\n druid:\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: ${MYSQL_USER:root}\n password: ${MYSQL_PWD:root}\n url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx_mp}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true\n', 'ae40865200e414d06d63d96f257f372e', '2022-12-16 10:44:25', '2024-02-22 15:09:20', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (12, 'pigx-xxl-job-admin-dev.yml', 'DEFAULT_GROUP', '# XXL-Job Admin 微服务配置\n# Actuator 监控配置\nmanagement:\n endpoints:\n web:\n base-path: /actuator\n health:\n mail:\n enabled: false\n\n# Spring 配置\nspring:\n # MVC 配置\n mvc:\n servlet:\n load-on-startup: 0\n static-path-pattern: /static/**\n\n # 静态资源配置\n web:\n resources:\n static-locations: classpath:/static/\n\n # Freemarker 模板引擎配置\n freemarker:\n template-loader-path: classpath:/templates/\n suffix: .ftl\n charset: UTF-8\n request-context-attribute: request\n settings:\n number_format: 0.##########\n new_builtin_class_resolver: safer\n\n # 数据源配置\n datasource:\n type: com.alibaba.druid.pool.DruidDataSource\n druid:\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: ${MYSQL_USER:root}\n password: ${MYSQL_PWD:root}\n url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx_job}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true\n\n # 邮件服务配置\n mail:\n host: smtp.qq.com\n port: 25\n username: xxx@qq.com\n from: xxx@qq.com\n password: xxx\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n socketFactory:\n class: javax.net.ssl.SSLSocketFactory\n\n# MyBatis 配置\nmybatis:\n mapper-locations: classpath:/mapper/**/*Mapper.xml\n\n# XXL-Job 配置\nxxl:\n job:\n # 访问令牌\n accessToken: default_token\n # 超时时间(秒)\n timeout: 3\n # 国际化 (zh_CN, zh_TC, en)\n i18n: zh_CN\n # 触发器线程池最大线程数\n triggerpool:\n fast:\n max: 300\n slow:\n max: 200\n # 调度批次大小\n schedule:\n batchsize: 100\n # 日志保留天数\n logretentiondays: 30\n\n# XXL-SSO 单点登录配置\nxxl-sso:\n token:\n key: xxl_job_login_token\n timeout: 604800000\n client:\n excluded:\n paths:\n login:\n path: /auth/login', 'e4e88bd4e91599ae03854a8dfe491786', '2022-12-16 10:44:25', '2026-05-01 23:02:41', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (13, 'pigx-report-platform-dev.yml', 'DEFAULT_GROUP', 'spring:\n datasource:\n type: com.alibaba.druid.pool.DruidDataSource\n druid:\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: ${MYSQL_USER:root}\n password: ${MYSQL_PWD:root}\n url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx_report}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true\n jpa:\n database-platform: org.hibernate.dialect.MySQLDialect\n\nsecurity:\n oauth2:\n client:\n ignore-urls:\n - /\n - /api/project/getData\n - /static/**\n - /api/project/get-file/*\n\n# 文件上传路径\ngv:\n img-path: /Users/lengleng/Downloads/img/\n', 'ce3fde4b4592cdd61feb2e443b2ddfe5', '2022-12-16 10:44:25', '2023-04-07 12:56:57', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (14, 'pigx-jimu-platform-dev.yml', 'DEFAULT_GROUP', 'spring:\n #配置静态资源\n mvc:\n static-path-pattern: /**\n resource:\n static-locations: classpath:/static/\n #配置数据库\n datasource:\n type: com.alibaba.druid.pool.DruidDataSource\n druid:\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: ${MYSQL_USER:root}\n password: ${MYSQL_PWD:root}\n url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx_bi}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true\n \n#JimuReport[minidao配置]\nminidao :\n base-package: org.jeecg.modules.jmreport.*,org.jeecg.modules.drag.*\n db-type: mysql\n#JimuReport[上传配置]\njeecg :\n jmreport:\n customPrePath: /api/jimu\n # 自动保存\n autoSave: true\n # 单位毫秒 默认5*60*1000 \n interval: 10000\n # local|minio|alioss\n uploadType: local\n # local\n path :\n #文件路径A\n upload: ~/jimu/data\n # alioss\n oss:\n endpoint: oss-cn-beijing.aliyuncs.com\n accessKey: ??\n secretKey: ??\n staticDomain: ??\n bucketName: ??\n # minio\n minio:\n minio_url: http://minio.jeecg.com\n minio_name: ??\n minio_pass: ??\n bucketName: ??\n#输出sql日志\nlogging:\n level:\n org.jeecg.modules.jmreport : debug', '608f41b13df1e7f5556fe7b8db0ff8be', '2022-12-16 10:44:25', '2024-01-30 20:44:36', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (14, 'pigx-jimu-platform-dev.yml', 'DEFAULT_GROUP', 'spring:\n #配置静态资源\n mvc:\n static-path-pattern: /**\n #配置数据库\n datasource:\n type: com.alibaba.druid.pool.DruidDataSource\n druid:\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: ${MYSQL_USER:root}\n password: ${MYSQL_PWD:root}\n url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx_bi}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true\n \n#JimuReport[minidao配置]\nminidao :\n base-package: org.jeecg.modules.jmreport.*,org.jeecg.modules.drag.*\n db-type: mysql\n#JimuReport[上传配置]\njeecg :\n jmreport:\n customPrePath: /api/jimu\n # 自动保存\n autoSave: true\n # 单位毫秒 默认5*60*1000 \n interval: 10000\n # local|minio|alioss\n uploadType: local\n # local\n path :\n #文件路径A\n upload: ~/jimu/data\n # alioss\n oss:\n endpoint: oss-cn-beijing.aliyuncs.com\n accessKey: ??\n secretKey: ??\n staticDomain: ??\n bucketName: ??\n # minio\n minio:\n minio_url: http://minio.jeecg.com\n minio_name: ??\n minio_pass: ??\n bucketName: ??\n#输出sql日志\nlogging:\n level:\n org.jeecg.modules.jmreport : debug', '898d86e9ae9110a0081f7d346487fe6a', '2022-12-16 10:44:25', '2024-01-30 20:44:36', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (15, 'pigx-app-server-biz-dev.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n type: com.alibaba.druid.pool.DruidDataSource\n druid:\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: ${MYSQL_USER:root}\n password: ${MYSQL_PWD:root}\n url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx_app}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&allowPublicKeyRetrieval=true', 'e4668a1854e92a5a666e83479ef7d26a', '2022-12-19 10:30:07', '2024-02-22 15:09:50', 'nacos', '127.0.0.1', '', 'public', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` (`id`, `data_id`, `group_id`, `content`, `md5`, `gmt_create`, `gmt_modified`, `src_user`, `src_ip`, `app_name`, `tenant_id`, `c_desc`, `c_use`, `effect`, `type`, `c_schema`, `encrypted_data_key`) VALUES (16, 'pigx-flow-biz-dev.yml', 'DEFAULT_GROUP', '# 数据源\nspring:\n datasource:\n type: com.alibaba.druid.pool.DruidDataSource\n druid:\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: ${MYSQL_USER:root}\n password: ${MYSQL_PWD:root}\n url: jdbc:mysql://${MYSQL_HOST:pigx-mysql}:${MYSQL_PORT:3306}/${MYSQL_DB:pigxx_flow}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&nullCatalogMeansCurrent=true\n\n# flowable 配置\nflowable:\n variable-json-mapper: jackson2', '233121e20445609e1ba378c3686ea7d5', '2023-07-28 13:41:32', '2023-07-28 13:43:36', 'nacos', '127.0.0.1', '', 'public', 'flowable 工作引擎', '', '', 'yaml', '', '');
COMMIT;
+53 -76
View File
@@ -107,29 +107,6 @@ INSERT INTO `gen_field_type` (`id`, `column_type`, `attr_type`, `package_name`,
INSERT INTO `gen_field_type` (`id`, `column_type`, `attr_type`, `package_name`, `default_form_type`, `default_query_form_type`, `create_time`, `create_by`, `update_time`, `update_by`, `del_flag`) VALUES (49,'bytea','String',NULL,'textarea','text','2026-04-07 20:47:09',NULL,'2026-04-07 20:47:09',NULL,'0');
COMMIT;
-- ----------------------------
-- Table structure for gen_form_conf
-- ----------------------------
DROP TABLE IF EXISTS `gen_form_conf`;
CREATE TABLE `gen_form_conf` (
`id` bigint NOT NULL COMMENT 'ID',
`ds_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '数据库名称',
`table_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '表名称',
`form_info` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '表单信息',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0',
`tenant_id` bigint DEFAULT NULL COMMENT '所属租户',
PRIMARY KEY (`id`) USING BTREE,
KEY `table_name` (`table_name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='表单配置';
-- ----------------------------
-- Records of gen_form_conf
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for gen_group
-- ----------------------------
@@ -139,8 +116,8 @@ CREATE TABLE `gen_group` (
`group_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '分组名称',
`group_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '分组描述',
`tenant_id` bigint NOT NULL COMMENT '租户ID',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
`create_time` datetime DEFAULT NULL COMMENT '创建人',
`update_time` datetime DEFAULT NULL COMMENT '修改人',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记',
@@ -151,10 +128,10 @@ CREATE TABLE `gen_group` (
-- Records of gen_group
-- ----------------------------
BEGIN;
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (1,'单表增删改查V2026001',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (2,'主子表增删改查V2026001',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (3,'树形表格增删改查V2026001',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (4,'左树右表增删改查V2026001',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (1,'单表增删改查V202611',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (2,'主子表增删改查V202611',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (3,'树形表格增删改查V202611',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
INSERT INTO `gen_group` (`id`, `group_name`, `group_desc`, `tenant_id`, `create_by`, `update_by`, `create_time`, `update_time`, `del_flag`) VALUES (4,'左树右表增删改查V202611',NULL,1,'admin','admin','2026-04-08 17:38:24','2026-04-08 17:38:24','0');
COMMIT;
-- ----------------------------
@@ -250,8 +227,8 @@ CREATE TABLE `gen_template` (
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT '删除标记',
`tenant_id` bigint NOT NULL COMMENT '租户ID',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT ' ' COMMENT '修改人',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '创建人',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT ' ' COMMENT '修改人',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='模板';
@@ -259,51 +236,51 @@ CREATE TABLE `gen_template` (
-- Records of gen_template
-- ----------------------------
BEGIN;
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (1,'ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #elsLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (2,'ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${entityClassName};\n\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (3,'ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (4,'实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (5,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (6,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (7,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (8,'api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n\n#if($ChildClassName)\n// ========== 子表相关接口 ==========\n\n/**\n * 删除子表数据\n * @param ids - 要删除的子表数据ID数组\n * @returns Promise<操作结果>\n */\nexport function delChildObj(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/child\',\n method: \'delete\',\n data: ids\n })\n}\n#end\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (9,'表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','表格V2026001','<template>\n <div class=\"layout-padding\">\n <div class=\"layout-padding-auto layout-padding-view\">\n#if($queryList)\n <!-- 查询表单区域 -->\n <el-row v-show=\"showSearch\">\n <el-form :model=\"state.queryForm\" ref=\"queryRef\" :inline=\"true\" @keyup.enter=\"getDataList\">\n#foreach($field in $queryList)\n#if($field.queryFormType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"state.queryForm.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option \n :label=\"item.label\" \n :value=\"item.value\" \n v-for=\"(item, index) in ${field.fieldDict}\" \n :key=\"index\"\n />\n#else\n <el-option label=\"请选择\" value=\"0\" />\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker\n type=\"date\"\n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"\n v-model=\"state.queryForm.${field.attrName}\"\n :value-format=\"dateStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}Range\">\n <el-date-picker\n type=\"daterange\"\n range-separator=\"至\"\n start-placeholder=\"开始日期\"\n end-placeholder=\"结束日期\"\n v-model=\"state.queryForm.${field.attrName}Range\"\n :value-format=\"dateStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetime\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker\n type=\"datetime\"\n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"\n v-model=\"state.queryForm.${field.attrName}\"\n :value-format=\"dateTimeStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetimerange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}Range\">\n <el-date-picker\n type=\"datetimerange\"\n range-separator=\"至\"\n start-placeholder=\"开始时间\"\n end-placeholder=\"结束时间\"\n v-model=\"state.queryForm.${field.attrName}Range\"\n :value-format=\"dateTimeStr\"\n />\n </el-form-item>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"state.queryForm.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio \n :label=\"item.value\" \n v-for=\"(item, index) in ${field.fieldDict}\" \n border \n :key=\"index\"\n >\n {{ item.label }}\n </el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>\n ${field.fieldComment}\n </el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n#else\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input \n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" \n v-model=\"state.queryForm.${field.attrName}\" \n />\n </el-form-item>\n#end\n#end\n <el-form-item>\n <el-button icon=\"search\" type=\"primary\" @click=\"getDataList\">\n 查询\n </el-button>\n <el-button icon=\"Refresh\" @click=\"resetQuery\">重置</el-button>\n </el-form-item>\n </el-form>\n </el-row>\n#end\n\n <!-- 操作按钮区域 -->\n <el-row>\n <div class=\"mb8\" style=\"width: 100Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (10,'表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','表单V2026001','<template>\n <el-dialog :title=\"form.${pk.attrName} ? \'编辑\' : \'新增\'\" v-model=\"visible\"\n :close-on-click-modal=\"false\" draggable>\n <el-form ref=\"dataFormRef\" :model=\"form\" :rules=\"dataRules\" label-width=\"90px\" v-loading=\"loading\">\n <el-row :gutter=\"24\">\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName})\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n#if($field.formType == \'text\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input type=\"textarea\" v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"form.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option :value=\"item.value\" :label=\"item.label\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\"></el-option>\n#else\n <el-option label=\"请选择\" value=\"0\"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" border :key=\"index\">{{ item.label }}</el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'checkbox\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-checkbox-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-checkbox :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\">{{ item.label }}</el-checkbox>\n#else\n <el-checkbox label=\"启用\" name=\"type\"></el-checkbox>\n <el-checkbox label=\"禁用\" name=\"type\"></el-checkbox>\n#end\n </el-checkbox-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"date\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" v-model=\"form.${field.attrName}\" :value-format=\"dateStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'datetime\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"datetime\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" v-model=\"form.${field.attrName}\" :value-format=\"dateTimeStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'daterange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"daterange\" range-separator=\"至\" start-placeholder=\"开始日期\" end-placeholder=\"结束日期\" v-model=\"form.${field.attrName}\" :value-format=\"dateStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'datetimerange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (11,'主子ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','主子ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport com.github.yulichang.extension.mapping.base.MPJDeepService;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\n\npublic interface ${ClassName}Service extends MPJDeepService<${ClassName}Entity> {\n Boolean saveDeep(${ClassName}Entity ${className});\n\n Boolean updateDeep(${ClassName}Entity ${className});\n\n Boolean removeDeep(Long[] ids);\n\n Boolean removeChild(Long[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (12,'主子ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','主子ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport org.springframework.transaction.annotation.Transactional;\nimport lombok.RequiredArgsConstructor;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveDeep(${ClassName}Entity ${className}) {\n baseMapper.insert(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert( ${childClassName});\n }\n\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateDeep(${ClassName}Entity ${className}) {\n baseMapper.updateById(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n#set($getChildPkName=$str.getProperty(${pk.attrName}))\n if (Objects.isNull(${childClassName}.$getChildPkName())) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert(${childClassName});\n } else {\n ${childClassName}Mapper.updateById(${childClassName});\n }\n }\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeDeep(Long[] ids) {\n baseMapper.deleteBatchIds(CollUtil.toList(ids));\n ${childClassName}Mapper.delete(Wrappers.<${ChildClassName}Entity>lambdaQuery().in(${ChildClassName}Entity::$str.getProperty($childField), ids));\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeChild(Long[] ids) {\n ${childClassName}Mapper.deleteBatchIds(CollUtil.toList(ids));\n return Boolean.TRUE;\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (13,'主实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','主实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\nimport org.apache.fesod.sheet.annotation.ExcelIgnore;\nimport com.github.yulichang.annotation.EntityMapping;\nimport java.util.List;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n @TableField(typeHandler = JacksonTypeHandler.class)\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n @ExcelIgnore\n @TableField(exist = false)\n @EntityMapping(thisField = \"$mainField\", joinField = \"$childField\")\n private List<${ChildClassName}Entity> ${childClassName}List;\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (14,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (15,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (16,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (17,'api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n\n#if($ChildClassName)\n// ========== 子表相关接口 ==========\n\n/**\n * 删除子表数据\n * @param ids - 要删除的子表数据ID数组\n * @returns Promise<操作结果>\n */\nexport function delChildObj(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/child\',\n method: \'delete\',\n data: ids\n })\n}\n#end\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (18,'子实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ChildClassName}Entity.java','子实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(\"${childTableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (19,'主子ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','主子ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.page(page, wrapper));\n }\n\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (20,'主子表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','主子表单V2026001','<template>\n <el-drawer :title=\"form.${pk.attrName} ? (detail ? \'详情\' : \'编辑\') : \'添加\'\" v-model=\"visible\" size=\"50%\">\n <el-form ref=\"dataFormRef\" :model=\"form\" :rules=\"dataRules\" :disabled=\"detail\" v-loading=\"loading\">\n <el-row :gutter=\"24\">\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName})\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n#if($field.formType == \'text\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-input v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-input type=\"textarea\" v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"form.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\">\n#if($field.fieldDict)\n <el-option :value=\"item.value\" :label=\"item.label\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\"></el-option>\n#else\n <el-option label=\"请选择\" value=\"0\"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" border :key=\"index\">{{ item.label }}</el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'checkbox\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-checkbox-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-checkbox :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\">{{ item.label }}</el-checkbox>\n#else\n <el-checkbox label=\"启用\" name=\"type\"></el-checkbox>\n <el-checkbox label=\"禁用\" name=\"type\"></el-checkbox>\n#end\n </el-checkbox-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"date\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" v-model=\"form.${field.attrName}\" :value-format=\"dateStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'datetime\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"datetime\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" v-model=\"form.${field.attrName}\" :value-format=\"dateTimeStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'daterange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"daterange\" range-separator=\"至\" start-placeholder=\"开始日期\" end-placeholder=\"结束日期\" v-model=\"form.${field.attrName}\" :value-format=\"dateStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'datetimerange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.fieldNaLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (21,'主子表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','主子表格V2026001','<template>\n <div class=\"layout-padding\">\n <div class=\"layout-padding-auto layout-padding-view\">\n#if($queryList)\n <el-row v-show=\"showSearch\">\n <el-form :model=\"state.queryForm\" ref=\"queryRef\" :inline=\"true\" @keyup.enter=\"getDataList\">\n#foreach($field in $queryList)\n#if($field.queryFormType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"state.queryForm.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option \n :label=\"item.label\" \n :value=\"item.value\" \n v-for=\"(item, index) in ${field.fieldDict}\" \n :key=\"index\">\n </el-option>\n#else\n <el-option label=\"请选择\">0</el-option>\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker\n type=\"date\"\n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"\n :value-format=\"dateStr\"\n v-model=\"state.queryForm.${field.attrName}\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}Range\">\n <el-date-picker\n type=\"daterange\"\n range-separator=\"至\"\n start-placeholder=\"开始日期\"\n end-placeholder=\"结束日期\"\n v-model=\"state.queryForm.${field.attrName}Range\"\n :value-format=\"dateStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetime\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker\n type=\"datetime\"\n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"\n :value-format=\"dateTimeStr\"\n v-model=\"state.queryForm.${field.attrName}\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetimerange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}Range\">\n <el-date-picker\n type=\"datetimerange\"\n range-separator=\"至\"\n start-placeholder=\"开始时间\"\n end-placeholder=\"结束时间\"\n v-model=\"state.queryForm.${field.attrName}Range\"\n :value-format=\"dateTimeStr\"\n />\n </el-form-item>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"state.queryForm.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio \n :label=\"item.value\" \n v-for=\"(item, index) in ${field.fieldDict}\" \n border \n :key=\"index\">\n {{ item.label }}\n </el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n#else\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input \n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" \n v-model=\"state.queryForm.${field.attrName}\"\n style=\"max-width: 180px\" \n />\n </el-form-item>\n#end\n#end\n <el-form-item>\n <el-button icon=\"search\" type=\"primary\" @click=\"getDataList\">\n 查询\n </el-button>\n <el-button icon=\"Refresh\" @click=\"resetQuery\">重置</el-button>\n </el-form-item>\n </el-form>\n </el-row>\n#end\n <el-row>\n <div class=\"mb8\" style=\"width: 100%\">\n <el-button \n icon=\"folder-adLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (22,'子MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV2026001','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (23,'树形ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','树形ControllerV2026001','package ${package}.${moduleName}.controller;\n\n#if($queryList)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 获取树形列表\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"获取树形列表\" , description = \"获取树形列表\" )\n @GetMapping(\"/tree\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Tree(@ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n return R.ok(${className}Service.buildTree(wrapper));\n }\n\n /**\n * 通过条件查询${tableComment}\n * @param ${className} 查询条件\n * @return R 对象列表\n */\n @Operation(summary = \"通过条件查询\" , description = \"通过条件查询对象\" )\n @GetMapping(\"/details\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (24,'树形ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','树形ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${entityClassName};\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List<Tree<${pk.attrType}>> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper);\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n boolean removeBatchByIds(List<${pk.attrType}> ids);\n} ','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (25,'树形ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','树形ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List<Tree<${pk.attrType}>> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper) {\n // 查询所有数据\n List<${ClassName}Entity> allList = list(wrapper);\n \n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n // 转换为TreeNode\n List<TreeNode<${pk.attrType}>> collect = allList.stream().map(getNodeFunction()).toList();\n\n // 使用TreeUtil构建树形结构,根节点ID为0\n#if($pk.attrType == \'Long\')\n return TreeUtil.build(collect, 0L);\n#else\n return TreeUtil.build(collect, 0);\n#end\n }\n\n /**\n * 获取TreeNode转换函数\n * @return TreeNode转换函数\n */\n @NotNull\n private Function<${ClassName}Entity, TreeNode<${pk.attrType}>> getNodeFunction() {\n return entity -> {\n TreeNode<${pk.attrType}> node = new TreeNode<>();\n node.setId(entity.$str.getProperty($pk.attrName)());\n node.setName(entity.$str.getProperty($nameField)());\n node.setParentId(entity.$str.getProperty($parentField)() != null ? entity.$str.getProperty($parentField)() : 0L);\n\n // 扩展属性\n Map<String, Object> extra = new HashMap<>();\n#foreach($field in $fieldList)\n extra.put(${ClassName}Entity.Fields.${field.attrName}, entity.$str.getProperty($field.attrName)());\n#end\n node.setExtra(extra);\n return node;\n };\n }\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public boolean removeBatchByIds(List<${pk.attrType}> ids) {\n if (CollUtil.isEmpty(ids)) {\n return true;\n }\n\n // 获取所有要删除的ID(包括子节点)\n List<${pk.attrType}> allDeleteIds = new ArrayList<>(ids);\n \n for (${pk.attrType} id : ids) {\n List<${pk.attrType}> childIds = getChildIdsRecursive(id);\n allDeleteIds.addAll(childIds);\n }\n\n // 去重\n allDeleteIds = allDeleteIds.stream().distinct().collect(Collectors.toList());\n\n // 批量删除\n return super.removeBatchByIds(allDeleteIds);\n }\n\n /**\n * 递归获取所有子节点ID\n * @param parentId 父节点ID\n * @return 子节点ID列表\n */\n private List<${pk.attrType}> getChildIdsRecursive(${pk.attrType} parentId) {\n List<${pk.attrType}> childIds = new ArrayList<>();\n \n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n wrapper.eq(${ClassName}Entity::$str.getProperty($parentField), parentId);\n wrapper.select(${ClassName}Entity::$str.getProperty($pk.attrName));\n \n List<${ClassName}Entity> children = list(wrapper);\n \n for (${ClassName}Entity child : children) {\n ${pk.attrType} childId = child.$str.getProperty($pk.attrName)();\n childIds.add(childId);\n // 递归获取子节点的子节点\n childIds.addAll(getChildIdsRecursive(childId));\n }\n \n return childIds;\n }\n} ','2026-04-08 17:38Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (26,'树形实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','树形实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.experimental.FieldNameConstants;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@FieldNameConstants\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (27,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (28,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (29,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (30,'树形api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','树形api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 树形表格CRUD接口 ==========\n\n/**\n * 获取树形列表数据\n * @param query - 查询参数对象\n * @returns Promise<树形数据>\n */\nexport function fetchTreeList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据(递归删除子节点)\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (31,'树形表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','树形表格V2026001','<template>\n <div class=\"layout-padding\">\n <div class=\"layout-padding-auto layout-padding-view\">\n#if($queryList)\n <!-- 查询表单区域 -->\n <el-row v-show=\"showSearch\">\n <el-form :model=\"state.queryForm\" ref=\"queryRef\" :inline=\"true\" @keyup.enter=\"getDataList\">\n#foreach($field in $queryList)\n#if($field.queryFormType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"state.queryForm.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option \n :label=\"item.label\" \n :value=\"item.value\" \n v-for=\"(item, index) in ${field.fieldDict}\" \n :key=\"index\"\n />\n#else\n <el-option label=\"请选择\" value=\"0\" />\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker \n type=\"date\" \n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" \n v-model=\"state.queryForm.${field.attrName}\"\n :value-format=\"dateStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetime\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker\n type=\"datetime\"\n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"\n v-model=\"state.queryForm.${field.attrName}\"\n :value-format=\"dateTimeStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}Range\">\n <el-date-picker\n type=\"daterange\"\n range-separator=\"至\"\n start-placeholder=\"开始日期\"\n end-placeholder=\"结束日期\"\n v-model=\"state.queryForm.${field.attrName}Range\"\n :value-format=\"dateStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetimerange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}Range\">\n <el-date-picker\n type=\"datetimerange\"\n range-separator=\"至\"\n start-placeholder=\"开始时间\"\n end-placeholder=\"结束时间\"\n v-model=\"state.queryForm.${field.attrName}Range\"\n :value-format=\"dateTimeStr\"\n />\n </el-form-item>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"state.queryForm.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio \n :label=\"item.value\" \n v-for=\"(item, index) in ${field.fieldDict}\" \n border \n :key=\"index\"\n >\n {{ item.label }}\n </el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>\n ${field.fieldComment}\n </el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n#else\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input \n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" \n v-model=\"state.queryForm.${field.attrName}\" \n />\n </el-form-item>\n#end\n#end\n <el-form-item>\n <el-button icon=\"search\" type=\"primary\" @click=\"getDataList\">\n 查询\n </el-button>\n <el-button icon=\"Refresh\" @click=\"resetQuery\">重置</el-button>\n </el-form-item>\n </el-form>\n </el-row>\n#end\n\n <!-- 操作按钮区域 -->\n <el-row>\n <div class=\"mb8\" stLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (32,'树形表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','树形表单V2026001','<template>\n <el-dialog :title=\"form.${pk.attrName} ? \'编辑\' : \'新增\'\" v-model=\"visible\"\n :close-on-click-modal=\"false\" draggable>\n <el-form ref=\"dataFormRef\" :model=\"form\" :rules=\"dataRules\" formDialogRef label-width=\"90px\" v-loading=\"loading\">\n <el-row :gutter=\"24\">\n <!-- 父级节点选择 -->\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n <el-form-item label=\"父级节点\" prop=\"${parentField}\">\n <el-tree-select\n v-model=\"form.${parentField}\"\n :data=\"parentNodes\"\n :props=\"treeSelectProps\"\n check-strictly\n :render-after-expand=\"false\"\n placeholder=\"请选择父级节点\"\n style=\"width: 100%\"\n clearable\n filterable\n />\n </el-form-item>\n </el-col>\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName} && $field.attrName != ${parentField})\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n#if($field.formType == \'text\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input type=\"textarea\" v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"form.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option :value=\"item.value\" :label=\"item.label\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\"></el-option>\n#else\n <el-option label=\"请选择\" value=\"0\"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" border :key=\"index\">{{ item.label }}</el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'checkbox\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-checkbox-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-checkbox :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\">{{ item.label }}</el-checkbox>\n#else\n <el-checkbox label=\"启用\" name=\"type\"></el-checkbox>\n <el-checkbox label=\"禁用\" name=\"type\"></el-checkbox>\n#end\n </el-checkbox-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"date\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" v-model=\"form.${field.attrName}\" :value-format=\"dateStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'datetime\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"datetime\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${fieLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (33,'ControllerV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','ControllerV2026001','package ${package}.${moduleName}.controller;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($childFieldGetter = $str.getProperty($childField))\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#foreach ($field in $queryList)\n#set($getAttrName=$str.getProperty($field.attrName))\n#set($var=\"${className}.$getAttrName()\")\n#if($field.attrType == \'String\')\n#set($expression=\"StrUtil.isNotBlank\")\n#else\n#set($expression=\"Objects.nonNull\")\n#end\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n if (ArrayUtil.isNotEmpty(${className}.${getAttrName}Range())) {\n wrapper.between(${ClassName}Entity::$getAttrName, ${className}.${getAttrName}Range()[0], ${className}.${getAttrName}Range()[1]);\n }\n#elseif($field.queryType == \'=\')\n wrapper.eq($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'like\' )\n wrapper.like($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'!-\' )\n wrapper.ne($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>\' )\n wrapper.gt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<\' )\n wrapper.lt($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'>=\' )\n wrapper.ge($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'<=\' )\n wrapper.le($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'left like\' )\n wrapper.likeLeft($expression($var),${ClassName}Entity::$getAttrName,$var);\n#elseif( $field.queryType == \'right like\' )\n wrapper.likeRight($expression($var),${ClassName}Entity::$getAttrName,$var);\n#end\n#end\n#if($childField)\n if (Objects.nonNull(${className}.$childFieldGetter())) {\n wrapper.eq(${ClassName}Entity::$str.getProperty($childField), ${className}.$childFieldGetter());\n }\n#end\n return R.ok(${className}Service.pLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (34,'ServiceV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','ServiceV2026001','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.extension.service.IService;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List<Tree<${childPkField.attrType}>> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper);\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName});\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n Boolean saveTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n Boolean updateTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n Boolean removeTreeByIds(${childPkField.attrType}[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (35,'ServiceImplV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','ServiceImplV2026001','package ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#set($childPkGetter = $str.getProperty($childPkField.attrName))\n#break\n#end\n#end\n#set($parentGetter = $str.getProperty($parentField))\n#set($childFieldGetter = $str.getProperty($childField))\n#if($childPkField.attrType == \'Long\')\n#set($treeRootId = \'0L\')\n#elseif($childPkField.attrType == \'String\')\n#set($treeRootId = \'\"0\"\')\n#else\n#set($treeRootId = \'0\')\n#end\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List<Tree<${childPkField.attrType}>> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper) {\n List<${ChildClassName}Entity> allList = ${childClassName}Mapper.selectList(wrapper);\n\n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n List<TreeNode<${childPkField.attrType}>> collect = allList.stream().map(getNodeFunction()).toList();\n return TreeUtil.build(collect, ${treeRootId});\n }\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n @Override\n public List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName}) {\n return ${childClassName}Mapper.selectList(Wrappers.query(${childClassName}));\n }\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(null, ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.insert(${childClassName}) > 0;\n }\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(${childClassName}.$str.getProperty($childPkField.attrName)(), ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.updateById(${childClassName}) > 0;\n }\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeTreeByIds(${childPkField.attrType}[] ids) {\n if (ArrayUtil.isEmpty(ids)) {\n return Boolean.TRUE;\n }\n\n for (${childPkField.attrType} id : ids) {\n if (hasChildNodes(id)) {\n throw new RuntimeException(\"当前节点存在子节点,不能删除\");\n }\n if (hasMainRecords(id)) {\n throw new RuntimeException(\"当前节点已关联主表数据,不能删除\");\n }\n }\n\n return ${childClassName}Mapper.deleteBatchLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (36,'实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(\"${tableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (37,'MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV2026001','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (38,'Mapper.xmlV2026001','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV2026001','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (39,'子实体V2026001','${entityPath}/src/main/java/${entityPackagePath}/${ChildClassName}Entity.java','子实体V2026001','package ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.extension.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#foreach($import in $importList)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(\"${childTableName}\")\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (40,'子MapperV2026001','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV2026001','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (41,'权限菜单V2026001','${backendPath}/menu/${functionName}_menu.sql','权限菜单V2026001','-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (42,'api.tsV2026001','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV2026001','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 树形CRUD接口 ==========\n\n/**\n * 获取树形列表数据\n * @param query - 查询参数对象\n * @returns Promise<树形数据>\n */\nexport function fetchTreeList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 获取树节点详情\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<树节点详情>\n */\nexport function getTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 新增树节点\n * @param obj - 要新增的树节点对象\n * @returns Promise<操作结果>\n */\nexport function addTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 更新树节点\n * @param obj - 要更新的树节点对象\n * @returns Promise<操作结果>\n */\nexport function putTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'put\',\n data: obj\n })\n}\n\n/**\n * 删除树节点\n * @param ids - 要删除的树节点ID数组\n * @returns Promise<操作结果>\n */\nexport function delTreeObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'delete\',\n data: ids\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n *\n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (43,'表格V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','表格V2026001','#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n<template>\n <div class=\"layout-padding\">\n <splitpanes>\n <pane size=\"15\">\n <div class=\"layout-padding-auto layout-padding-view\">\n <el-scrollbar>\n <div class=\"mb8\" style=\"display: flex; align-items: center; justify-content: space-between;\">\n <span style=\"font-weight: 500;\">左侧节点</span>\n <div>\n <el-tooltip :content=\"currentTreeId ? \'新增子节点\' : \'新增根节点\'\" placement=\"top\">\n <el-button link type=\"primary\" icon=\"FolderAdd\" @click=\"handleAddTree\" />\n </el-tooltip>\n </div>\n </div>\n <el-tree\n ref=\"treeRef\"\n :data=\"treeData\"\n :props=\"treeProps\"\n node-key=\"${childPkField.attrName}\"\n highlight-current\n default-expand-all\n :expand-on-click-node=\"false\"\n @node-click=\"handleTreeNodeClick\"\n >\n <template #default=\"{ node, data }\">\n <span style=\"flex: 1; display: flex; align-items: center; justify-content: space-between;\">\n <span>{{ node.label }}</span>\n <span v-if=\"currentTreeId === data.${childPkField.attrName}\" style=\"margin-left: 8px;\">\n <el-tooltip content=\"编辑\" placement=\"top\">\n <el-button link type=\"primary\" icon=\"Edit\" size=\"small\" @click.stop=\"handleEditTree\" />\n </el-tooltip>\n <el-tooltip content=\"删除\" placement=\"top\">\n <el-button link type=\"danger\" icon=\"Delete\" size=\"small\" @click.stop=\"handleDeleteTree\" />\n </el-tooltip>\n </span>\n </span>\n </template>\n </el-tree>\n </el-scrollbar>\n </div>\n </pane>\n <pane class=\"ml8\">\n <div class=\"layout-padding-auto layout-padding-view\">\n#if($queryList)\n <el-row v-show=\"showSearch\">\n <el-form :model=\"state.queryForm\" ref=\"queryRef\" :inline=\"true\" @keyup.enter=\"getDataList\">\n#foreach($field in $queryList)\n#if($field.attrName != ${childField})\n#if($field.queryFormType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"state.queryForm.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option \n :label=\"item.label\" \n :value=\"item.value\" \n v-for=\"(item, index) in ${field.fieldDict}\" \n :key=\"index\"\n />\n#else\n <el-option label=\"请选择\" value=\"0\" />\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker\n type=\"date\"\n placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"\n v-model=\"state.queryForm.${field.attrName}\"\n :value-format=\"dateStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}Range\">\n <el-date-picker\n type=\"daterange\"\n range-separator=\"至\"\n start-placeholder=\"开始日期\"\n end-placeholder=\"结束日期\"\n v-model=\"state.queryForm.${field.attrName}Range\"\n :value-format=\"dateStr\"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetime\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker\n type=\"datetime\"\n placeholder=\"请输入#if(${field.fieldComment})${field.fieLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (44,'树表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/tree-form.vue','树表单V2026001','#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($fieldDict=[])\n#foreach($field in $childFieldList)\n#if($field.fieldDict)\n#set($void=$fieldDict.add($field.fieldDict))\n#end\n#end\n<template>\n <el-dialog :title=\"form.${childPkField.attrName} ? \'编辑\' : \'新增\'\" v-model=\"visible\"\n :close-on-click-modal=\"false\" draggable>\n <el-form ref=\"dataFormRef\" :model=\"form\" :rules=\"dataRules\" label-width=\"90px\" v-loading=\"loading\">\n <el-row :gutter=\"24\">\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n <el-form-item label=\"父级节点\" prop=\"${parentField}\">\n <el-tree-select\n v-model=\"form.${parentField}\"\n :data=\"parentNodes\"\n :props=\"treeSelectProps\"\n check-strictly\n :render-after-expand=\"false\"\n placeholder=\"请选择父级节点\"\n style=\"width: 100%\"\n clearable\n filterable\n />\n </el-form-item>\n </el-col>\n#foreach($field in $childFieldList)\n#if($field.attrName != ${childPkField.attrName} && $field.attrName != ${parentField})\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n#if($field.formType == \'text\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input type=\"textarea\" v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"form.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option :value=\"item.value\" :label=\"item.label\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\"></el-option>\n#else\n <el-option label=\"请选择\" value=\"0\"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" border :key=\"index\">{{ item.label }}</el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'checkbox\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-checkbox-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-checkbox :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\">{{ item.label }}</el-checkbox>\n#else\n <el-checkbox label=\"启用\" name=\"type\"></el-checkbox>\n <el-checkbox label=\"禁用\" name=\"type\"></el-checkbox>\n#end\n </el-checkbox-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"date\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" v-model=\"form.${field.attrName}\" :value-format=\"dateStr\"></el-date-picker>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'datetime\')\n <el-form-item label=\Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (45,'主表单V2026001','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','主表单V2026001','#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($relationFieldLabel = $childField)\n#foreach($field in $fieldList)\n#if($field.attrName == $childField)\n#if($field.fieldComment)\n#set($relationFieldLabel = $field.fieldComment)\n#else\n#set($relationFieldLabel = $field.attrName)\n#end\n#break\n#end\n#end\n#set($fieldDict=[])\n#foreach($field in $formList)\n#if($field.fieldDict)\n#set($void=$fieldDict.add($field.fieldDict))\n#end\n#end\n<template>\n <el-dialog :title=\"form.${pk.attrName} ? \'编辑\' : \'新增\'\" v-model=\"visible\"\n :close-on-click-modal=\"false\" draggable>\n <el-form ref=\"dataFormRef\" :model=\"form\" :rules=\"dataRules\" label-width=\"90px\" v-loading=\"loading\">\n <el-row :gutter=\"24\">\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n <el-form-item label=\"${relationFieldLabel}\" prop=\"${childField}\">\n <el-tree-select\n v-model=\"form.${childField}\"\n :data=\"treeNodes\"\n :props=\"treeSelectProps\"\n check-strictly\n :render-after-expand=\"false\"\n placeholder=\"请选择${relationFieldLabel}\"\n style=\"width: 100%\"\n clearable\n filterable\n />\n </el-form-item>\n </el-col>\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName} && $field.attrName != ${childField})\n#if($formLayout == 1)\n <el-col :span=\"24\" class=\"mb20\">\n#elseif($formLayout == 2)\n <el-col :span=\"12\" class=\"mb20\">\n#end\n#if($field.formType == \'text\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-input type=\"textarea\" v-model=\"form.${field.attrName}\" placeholder=\"请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-select v-model=\"form.${field.attrName}\" placeholder=\"请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\">\n#if($field.fieldDict)\n <el-option :value=\"item.value\" :label=\"item.label\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\"></el-option>\n#else\n <el-option label=\"请选择\" value=\"0\"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-radio-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-radio :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" border :key=\"index\">{{ item.label }}</el-radio>\n#else\n <el-radio label=\"${field.fieldComment}\" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'checkbox\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-checkbox-group v-model=\"form.${field.attrName}\">\n#if($field.fieldDict)\n <el-checkbox :label=\"item.value\" v-for=\"(item, index) in ${field.fieldDict}\" :key=\"index\">{{ item.label }}</el-checkbox>\n#else\n <el-checkbox label=\"启用\" name=\"type\"></el-checkbox>\n <el-checkbox label=\"禁用\" name=\"type\"></el-checkbox>\n#end\n </el-checkbox-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'date\')\n <el-form-item label=\"#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end\" prop=\"${field.attrName}\">\n <el-date-picker type=\"date\" placeholder=\"请选择#if(${field.fieldComment})${field.fiLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (1,'ControllerV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','ControllerV202611','## 查询条件按需导入:字符串字段和组织关键字用 StrUtil,其余类型用 Objects;复选框与日期范围两者都不用\n#set($needStrUtil = false)\n#set($needObjects = false)\n#foreach($field in $queryList)\n#if($field.formType == \'checkbox\' || $field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#elseif($field.formType.startsWith(\'org-\') || $field.attrType == \'String\')\n#set($needStrUtil = true)\n#else\n#set($needObjects = true)\n#end\n#end\n#set($hasOrgQuery = false)\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrgQuery = true)\n#end\n#end\n## 组织字段以 [{id,name}] JSON 存储,按 ${attrName}Keyword 对整列模糊查询,分页和导出共用\n#macro(orgKeywordParams)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), @RequestParam(required = false) String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordDecl)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordArgs)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordFilters)\n#if($hasOrgQuery)\n // 组织字段对整列 JSON 做 LIKE:关键字为数字或 id/name 时会误命中,需精确匹配时改用数据库 JSON 函数\n#end\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n wrapper.like(StrUtil.isNotBlank(${field.attrName}Keyword), ${ClassName}Entity::$str.getProperty($field.attrName), ${field.attrName}Keyword);\n#end\n#end\n#end\n#set($permissionPrefix = \"${moduleName}_${functionName}\")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\npackage ${package}.${moduleName}.controller;\n\n#if($needStrUtil)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n#if($needObjects)\nimport java.util.Objects;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"${permissionPrefix}_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'${permissionPrefix}_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}#orgKeywordParams()) {\n return R.ok(${className}Service.page(page, buildQueryWrapper(${className}#orgKeywordArgs())));\n }\n\n /**\n * 列表与导出共用的查询条件,保证导出结果与列表一致\n */\n private LambdaQueryWrapper<${ClassName}Entity> buildQueryWrapper(${ClassName}Entity ${className}#orgKeywordDecl()) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (2,'ServiceV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','ServiceV202611','package ${package}.${moduleName}.service;\n\nimport com.baomidou.mybatisplus.spring.service.IService;\nimport ${entityClassName};\n\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (3,'ServiceImplV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','ServiceImplV202611','package ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (4,'实体V202611','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','实体V202611','#set($hasOrg = false)\n#foreach($field in $fieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\npackage ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.spring.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($hasOrg)\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport ${package}.common.core.util.OrgRef;\nimport org.apache.fesod.sheet.annotation.ExcelIgnore;\nimport java.util.List;\n#end\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\n#if(!$import.startsWith(\'java.lang.\'))\nimport $import;\n#end\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(value = \"${tableName}\"#if($hasOrg), autoResultMap = true#end)\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.formType.startsWith(\'org-\'))\n @TableField(typeHandler = JacksonTypeHandler.class#if($field.autoFill == \'INSERT\' || $field.autoFill == \'INSERT_UPDATE\' || $field.autoFill == \'UPDATE\'), fill = FieldFill.${field.autoFill}#end)\n @ExcelIgnore\n#elseif($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType.startsWith(\'org-\'))\n private List<OrgRef> $field.attrName;\n#elseif($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (5,'MapperV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV202611','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (6,'Mapper.xmlV202611','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV202611','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"#if($field.formType.startsWith(\'org-\')) typeHandler=\"com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler\"#end/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (7,'权限菜单V202611','${backendPath}/menu/${functionName}_menu.sql','权限菜单V202611','#set($permissionPrefix = \"${moduleName}_${functionName}\")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\n-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'${permissionPrefix}_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'${permissionPrefix}_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'${permissionPrefix}_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'${permissionPrefix}_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'${permissionPrefix}_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (8,'api.tsV202611','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV202611','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n\n#if($ChildClassName)\n// ========== 子表相关接口 ==========\n\n/**\n * 删除子表数据\n * @param ids - 要删除的子表数据ID数组\n * @returns Promise<操作结果>\n */\nexport function delChildObj(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/child\',\n method: \'delete\',\n data: ids\n })\n}\n#end\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (9,'表格V202611','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','表格V202611','## 有字典字段时才导入 useDict\n#set($hasDictField = false)\n#foreach($field in $queryList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n#foreach($field in $gridList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n#set($permissionPrefix = "${moduleName}_${functionName}")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\n<template>\n <div class="layout-padding">\n <div class="layout-padding-auto layout-padding-view">\n#if($queryList)\n <!-- 查询表单区域 -->\n <el-row v-show="showSearch">\n <el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))#set($queryKey = "${field.attrName}Keyword")#else#set($queryKey = $field.attrName)#end\n#if($field.queryFormType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-select v-model="state.queryForm.${field.attrName}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option \n :label="item.label" \n :value="item.value" \n v-for="(item, index) in ${field.fieldDict}" \n :key="index"\n />\n#else\n <el-option label="请选择" value="0" />\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-date-picker\n type="date"\n placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"\n v-model="state.queryForm.${field.attrName}"\n :value-format="dateStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}Range">\n <el-date-picker\n type="daterange"\n range-separator="至"\n start-placeholder="开始日期"\n end-placeholder="结束日期"\n v-model="state.queryForm.${field.attrName}Range"\n :value-format="dateStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetime\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-date-picker\n type="datetime"\n placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"\n v-model="state.queryForm.${field.attrName}"\n :value-format="dateTimeStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetimerange\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}Range">\n <el-date-picker\n type="datetimerange"\n range-separator="至"\n start-placeholder="开始时间"\n end-placeholder="结束时间"\n v-model="state.queryForm.${field.attrName}Range"\n :value-format="dateTimeStr"\n />\n </el-form-item>\n#elseif($field.formType == \'radio\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-radio-group v-model="state.queryForm.${field.attrName}">\n#if($field.fieldDict)\n <el-radio \n :label="item.value" \n v-for="(item, index) in ${field.fieldDict}" \n border \n :key="index"\n >\n {{ item.label }}\n </el-radio>\n#else\n <el-radio label="${field.fieldComment}" border>\n ${field.fieldComment}\n </el-radio>\n#end\n </el-rLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (10,'表单V202611','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','表单V202611','## 有字典字段时才导入 useDict\n#set($hasDictField = false)\n#foreach($field in $formList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n#set($hasDuplicate = false)\n#foreach($field in $formList)\n#if($field.formValidator == \'duplicate\')\n#set($hasDuplicate = true)\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n#set($hasOrg = false)\n#foreach($field in $formList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\n<template>\n <el-dialog :title="form.${pk.attrName} ? \'编辑\' : \'新增\'" v-model="visible"\n :close-on-click-modal="false" draggable>\n <el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="90px" v-loading="loading">\n <el-row :gutter="24">\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName})\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n#if($field.formType.startsWith(\'org-\'))\n#set($orgType = $field.formType.replace(\'org-\', \'\').replace(\'-multiple\', \'\'))\n <el-form-item label="${field.fieldComment}" prop="${field.attrName}">\n <OrgSelect v-model="form.${field.attrName}" value-type="item" type="${orgType}" :multiple="#if($field.formType.endsWith(\'-multiple\'))true#else false#end" label="${field.fieldComment}" :disabled="loading" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'switch\')\n#switchValues($field)\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-switch :model-value="String(form.${field.attrName}) === String($switchOn)" @change="(checked) => (form.${field.attrName} = checked ? $switchOn : $switchOff)" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'text\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input type="textarea" v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-select v-model="form.${field.attrName}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option :value="item.value" :label="item.label" v-for="(item, index) in ${field.fieldDict}" :key="index"></el-option>\n#else\n <el-option label="请选择" value="0"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-radio-group v-model="form.${field.attrName}">\n#if($field.fieldDict)\n <el-radio :label="item.value" v-for="(item, index) in ${field.fieldDict}" border :key="index">{{ item.label }}</el-radio>\n#else\n <el-radio label="${field.fieldComment}" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'checkbox\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-checkbox-group v-model="form.${field.attrName}">\n#if($field.fieldDict)\n <el-checkbox :label="item.value" v-for="(item, index) in ${field.fieldDict}" :key="index">{{ item.label }}</el-checkbox>\n#else\n <el-checkbox label="启用"Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (11,'主子ServiceV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','主子ServiceV202611','#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPk = $field)\n#end\n#end\npackage ${package}.${moduleName}.service;\n\nimport com.github.yulichang.extension.mapping.base.MPJDeepService;\nimport ${entityClassName};\n\npublic interface ${ClassName}Service extends MPJDeepService<${ClassName}Entity> {\n Boolean saveDeep(${ClassName}Entity ${className});\n\n Boolean updateDeep(${ClassName}Entity ${className});\n\n Boolean removeDeep(${pk.attrType}[] ids);\n\n Boolean removeChild(${childPk.attrType}[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (12,'主子ServiceImplV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','主子ServiceImplV202611','#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPk = $field)\n#end\n#end\npackage ${package}.${moduleName}.service.impl;\n\nimport com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport org.springframework.stereotype.Service;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport org.springframework.transaction.annotation.Transactional;\nimport lombok.RequiredArgsConstructor;\nimport java.util.Objects;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveDeep(${ClassName}Entity ${className}) {\n baseMapper.insert(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert( ${childClassName});\n }\n\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateDeep(${ClassName}Entity ${className}) {\n baseMapper.updateById(${className});\n for (${ChildClassName}Entity ${childClassName} : ${className}.get${ChildClassName}List()) {\n#set($getChildPkName=$str.getProperty(${childPk.attrName}))\n if (Objects.isNull(${childClassName}.$getChildPkName())) {\n ${childClassName}.$str.setProperty($childField)(${className}.$str.getProperty($mainField)());\n ${childClassName}Mapper.insert(${childClassName});\n } else {\n ${childClassName}Mapper.updateById(${childClassName});\n }\n }\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeDeep(${pk.attrType}[] ids) {\n List<${ClassName}Entity> records = baseMapper.selectBatchIds(CollUtil.toList(ids));\n if (CollUtil.isEmpty(records)) {\n return Boolean.TRUE;\n }\n ${childClassName}Mapper.delete(Wrappers.<${ChildClassName}Entity>lambdaQuery()\n .in(${ChildClassName}Entity::$str.getProperty($childField), records.stream()\n .map(${ClassName}Entity::$str.getProperty($mainField)).collect(Collectors.toList())));\n baseMapper.deleteBatchIds(CollUtil.toList(ids));\n return Boolean.TRUE;\n }\n\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeChild(${childPk.attrType}[] ids) {\n ${childClassName}Mapper.deleteBatchIds(CollUtil.toList(ids));\n return Boolean.TRUE;\n }\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (13,'主实体V202611','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','主实体V202611','#set($hasOrg = false)\n#foreach($field in $fieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\npackage ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.spring.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($hasOrg)\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport ${package}.common.core.util.OrgRef;\n#end\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\n#if(!$import.startsWith(\'java.lang.\'))\nimport $import;\n#end\n#end\nimport org.apache.fesod.sheet.annotation.ExcelIgnore;\nimport com.github.yulichang.annotation.EntityMapping;\nimport java.util.List;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(value = \"${tableName}\"#if($hasOrg), autoResultMap = true#end)\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.formType.startsWith(\'org-\'))\n @TableField(typeHandler = JacksonTypeHandler.class#if($field.autoFill == \'INSERT\' || $field.autoFill == \'INSERT_UPDATE\' || $field.autoFill == \'UPDATE\'), fill = FieldFill.${field.autoFill}#end)\n @ExcelIgnore\n#elseif($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType.startsWith(\'org-\'))\n private List<OrgRef> $field.attrName;\n#elseif($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n @TableField(typeHandler = JacksonTypeHandler.class)\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n @ExcelIgnore\n @TableField(exist = false)\n @EntityMapping(thisField = \"$mainField\", joinField = \"$childField\")\n private List<${ChildClassName}Entity> ${childClassName}List;\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (14,'MapperV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV202611','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (15,'Mapper.xmlV202611','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV202611','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"#if($field.formType.startsWith(\'org-\')) typeHandler=\"com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler\"#end/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (16,'权限菜单V202611','${backendPath}/menu/${functionName}_menu.sql','权限菜单V202611','#set($permissionPrefix = \"${moduleName}_${functionName}\")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\n-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'${permissionPrefix}_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'${permissionPrefix}_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'${permissionPrefix}_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'${permissionPrefix}_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'${permissionPrefix}_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (17,'api.tsV202611','${frontendPath}/src/api/${moduleName}/${functionName}.ts','api.tsV202611','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n\n#if($ChildClassName)\n// ========== 子表相关接口 ==========\n\n/**\n * 删除子表数据\n * @param ids - 要删除的子表数据ID数组\n * @returns Promise<操作结果>\n */\nexport function delChildObj(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/child\',\n method: \'delete\',\n data: ids\n })\n}\n#end\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (18,'子实体V202611','${entityPath}/src/main/java/${entityPackagePath}/${ChildClassName}Entity.java','子实体V202611','#set($hasOrg = false)\n#foreach($field in $childFieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\npackage ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.spring.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($hasOrg)\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport ${package}.common.core.util.OrgRef;\nimport java.util.List;\n#end\n#set($childImports = [])\n#foreach($field in $childFieldList)\n#if($field.packageName && !$field.packageName.startsWith(\'java.lang.\') && !$childImports.contains($field.packageName))\n#set($void = $childImports.add($field.packageName))\n#end\n#end\n#foreach($import in $childImports)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(value = \"${childTableName}\"#if($hasOrg), autoResultMap = true#end)\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.formType.startsWith(\'org-\'))\n @TableField(typeHandler = JacksonTypeHandler.class#if($field.autoFill == \'INSERT\' || $field.autoFill == \'INSERT_UPDATE\' || $field.autoFill == \'UPDATE\'), fill = FieldFill.${field.autoFill}#end)\n#elseif($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType.startsWith(\'org-\'))\n private List<OrgRef> $field.attrName;\n#elseif($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (19,'主子ControllerV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','主子ControllerV202611','## 查询条件按需导入:字符串字段和组织关键字用 StrUtil,其余类型用 Objects;复选框与日期范围两者都不用\n#set($needStrUtil = false)\n#set($needObjects = false)\n#foreach($field in $queryList)\n#if($field.formType == \'checkbox\' || $field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#elseif($field.formType.startsWith(\'org-\') || $field.attrType == \'String\')\n#set($needStrUtil = true)\n#else\n#set($needObjects = true)\n#end\n#end\n#set($hasOrgQuery = false)\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrgQuery = true)\n#end\n#end\n## 组织字段以 [{id,name}] JSON 存储,按 ${attrName}Keyword 对整列模糊查询,分页和导出共用\n#macro(orgKeywordParams)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), @RequestParam(required = false) String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordDecl)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordArgs)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordFilters)\n#if($hasOrgQuery)\n // 组织字段对整列 JSON 做 LIKE:关键字为数字或 id/name 时会误命中,需精确匹配时改用数据库 JSON 函数\n#end\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n wrapper.like(StrUtil.isNotBlank(${field.attrName}Keyword), ${ClassName}Entity::$str.getProperty($field.attrName), ${field.attrName}Keyword);\n#end\n#end\n#end\n#set($permissionPrefix = \"${moduleName}_${functionName}\")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPk = $field)\n#end\n#end\npackage ${package}.${moduleName}.controller;\n\n#if($needStrUtil)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n#if($needObjects)\nimport java.util.Objects;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping(\"/${functionName}\" )\n@Tag(description = \"${functionName}\" , name = \"${tableComment}管理\" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = \"分页查询\" , description = \"分页查询\" )\n @GetMapping(\"/page\" )\n #if($isSpringBoot3)\n @HasPermission(\"${permissionPrefix}_view\")\n #else\n @PreAuthorize(\"@pms.hasPermission(\'${permissionPrefix}_view\')\" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}#orgKeywordParams()) {\n return R.ok(${className}Service.page(page, buildQueryWrapper(${className}#orgKeywordArgs())));\n }\n\n /**\n * 列表与导出共用的查询条件,保证导出结果与列表一致\n */\n private LambdaQueryWrapper<${ClassName}Entity> buildQueryWrapper(${ClassName}Entity ${className}#orgKeywordDecl()) {\Line truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (20,'主子表单V202611','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','主子表单V202611','## 有字典字段时才导入 useDict\n#set($hasDictField = false)\n#foreach($field in $formList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n#set($hasDuplicate = false)\n#foreach($field in $formList)\n#if($field.formValidator == \'duplicate\')\n#set($hasDuplicate = true)\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n#set($hasOrg = false)\n#foreach($field in $formList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\n#foreach($field in $childFieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\n<template>\n <el-drawer :title="form.${pk.attrName} ? (detail ? \'详情\' : \'编辑\') : \'添加\'" v-model="visible" size="50%">\n <el-form ref="dataFormRef" :model="form" :rules="dataRules" :disabled="detail" v-loading="loading">\n <el-row :gutter="24">\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName})\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n#if($field.formType.startsWith(\'org-\'))\n#set($orgType = $field.formType.replace(\'org-\', \'\').replace(\'-multiple\', \'\'))\n <el-form-item label="${field.fieldComment}" prop="${field.attrName}">\n <OrgSelect v-model="form.${field.attrName}" value-type="item" type="${orgType}" :multiple="#if($field.formType.endsWith(\'-multiple\'))true#else false#end" label="${field.fieldComment}" :disabled="loading || detail" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'switch\')\n#switchValues($field)\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-switch :model-value="String(form.${field.attrName}) === String($switchOn)" @change="(checked) => (form.${field.attrName} = checked ? $switchOn : $switchOff)" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'text\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end" prop="${field.attrName}">\n <el-input v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end" prop="${field.attrName}">\n <el-input type="textarea" v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end" prop="${field.attrName}">\n <el-select v-model="form.${field.attrName}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end">\n#if($field.fieldDict)\n <el-option :value="item.value" :label="item.label" v-for="(item, index) in ${field.fieldDict}" :key="index"></el-option>\n#else\n <el-option label="请选择" value="0"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end" prop="${field.attrName}">\n <el-radio-group v-model="form.${field.attrName}">\n#if($field.fieldDict)\n <el-radio :label="item.value" v-for="(item, index) in ${field.fieldDict}" border :key="index">{{ item.label }}</el-radio>\n#else\n <el-radio label="${field.fieldComment}" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'checkbox\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.fieldName}#end" prop="${field.attrName}">\n <el-checkbox-group v-model="form.${field.attrName}">\n#if($field.fieldDict)\n <el-checkbox :label="item.value" v-fLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (21,'主子表格V202611','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','主子表格V202611','## 有字典字段时才导入 useDict\n#set($hasDictField = false)\n#foreach($field in $queryList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n#foreach($field in $gridList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n#set($permissionPrefix = "${moduleName}_${functionName}")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\n<template>\n <div class="layout-padding">\n <div class="layout-padding-auto layout-padding-view">\n#if($queryList)\n <el-row v-show="showSearch">\n <el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))#set($queryKey = "${field.attrName}Keyword")#else#set($queryKey = $field.attrName)#end\n#if($field.queryFormType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-select v-model="state.queryForm.${field.attrName}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option \n :label="item.label" \n :value="item.value" \n v-for="(item, index) in ${field.fieldDict}" \n :key="index">\n </el-option>\n#else\n <el-option label="请选择">0</el-option>\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-date-picker\n type="date"\n placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"\n :value-format="dateStr"\n v-model="state.queryForm.${field.attrName}"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}Range">\n <el-date-picker\n type="daterange"\n range-separator="至"\n start-placeholder="开始日期"\n end-placeholder="结束日期"\n v-model="state.queryForm.${field.attrName}Range"\n :value-format="dateStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetime\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-date-picker\n type="datetime"\n placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"\n :value-format="dateTimeStr"\n v-model="state.queryForm.${field.attrName}"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetimerange\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}Range">\n <el-date-picker\n type="datetimerange"\n range-separator="至"\n start-placeholder="开始时间"\n end-placeholder="结束时间"\n v-model="state.queryForm.${field.attrName}Range"\n :value-format="dateTimeStr"\n />\n </el-form-item>\n#elseif($field.formType == \'radio\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-radio-group v-model="state.queryForm.${field.attrName}">\n#if($field.fieldDict)\n <el-radio \n :label="item.value" \n v-for="(item, index) in ${field.fieldDict}" \n border \n :key="index">\n {{ item.label }}\n </el-radio>\n#else\n <el-radio label="${field.fieldComment}" border>${field.fieldComment}</el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n#else\n <elLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (22,'子MapperV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV202611','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (23,'树形ControllerV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','树形ControllerV202611','## 查询条件按需导入:字符串字段用 StrUtil,其余类型用 Objects;复选框与日期范围两者都不用\n#set($needStrUtil = false)\n#set($needObjects = false)\n#foreach($field in $queryList)\n#if($field.formType == \'checkbox\' || $field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#elseif($field.formType.startsWith(\'org-\') || $field.attrType == \'String\')\n#set($needStrUtil = true)\n#else\n#set($needObjects = true)\n#end\n#end\n#set($hasOrgQuery = false)\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrgQuery = true)\n#end\n#end\n## 组织字段以 [{id,name}] JSON 存储,按 ${attrName}Keyword 对整列模糊查询,分页和导出共用\n#macro(orgKeywordParams)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), @RequestParam(required = false) String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordDecl)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordArgs)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordFilters)\n#if($hasOrgQuery)\n // 组织字段对整列 JSON 做 LIKE:关键字为数字或 id/name 时会误命中,需精确匹配时改用数据库 JSON 函数\n#end\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n wrapper.like(StrUtil.isNotBlank(${field.attrName}Keyword), ${ClassName}Entity::$str.getProperty($field.attrName), ${field.attrName}Keyword);\n#end\n#end\n#end\npackage ${package}.${moduleName}.controller;\n\n#if($queryList)\n#if($needStrUtil)\nimport cn.hutool.core.util.StrUtil;\n#end\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n#if($needObjects)\nimport java.util.Objects;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@RestController\n@RequiredArgsConstructor\n@RequestMapping("/${functionName}" )\n@Tag(description = "${functionName}" , name = "${tableComment}管理" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 获取树形列表\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = "获取树形列表" , description = "获取树形列表" )\n @GetMapping("/tree" )\n #if($isSpringBoot3)\n @HasPermission("$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view")\n #else\n @PreAuthorize("@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')" )\n #end\n public R get${ClassName}Tree(@ParameterObject ${ClassName}Entity ${className}#orgKeywordParams()) {\n return R.ok(${className}Service.buildTree(buildQueryWrapper(${className}#orgKeywordArgs())));\n }\n\n /**\n * 列表与导出共用的查询条件,保证导出结果与列表一致\n */\n private LambdaQueryWrapper<${ClassName}Entity> buildQueryWrapper(${ClassName}Entity ${className}#orgKeywordDecl()) {\n LambdaQueryWrapper<${ClassName}Entity> wrapper = Wrappers.lambdaQuery();\n#orgKeywordFilters()\n#foreach ($field in $queryList)\n#if(!$field.formType.startsWith(\'org-\'))\n#set($getAttrName=$str.getProperty($field.attLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (24,'树形ServiceV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','树形ServiceV202611','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.spring.service.IService;\nimport ${entityClassName};\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List<Tree<${pk.attrType}>> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper);\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n boolean removeBatchByIds(List<${pk.attrType}> ids);\n} ','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (25,'树形ServiceImplV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','树形ServiceImplV202611','## 根节点 ID 按主键类型生成,与 Tree<${pk.attrType}> 类型一致\n#if($pk.attrType == \'Long\')\n#set($treeRootId = \'0L\')\n#elseif($pk.attrType == \'String\')\n#set($treeRootId = \'"0"\')\n#else\n#set($treeRootId = \'0\')\n#end\npackage ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport jakarta.validation.constraints.NotNull;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List<Tree<${pk.attrType}>> buildTree(LambdaQueryWrapper<${ClassName}Entity> wrapper) {\n // 查询命中节点,再按层补齐祖先,保留完整树路径\n List<${ClassName}Entity> allList = list(wrapper);\n \n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n Map<${pk.attrType}, ${ClassName}Entity> nodes = new LinkedHashMap<>();\n allList.forEach(entity -> nodes.put(entity.$str.getProperty($pk.attrName)(), entity));\n List<${ClassName}Entity> level = allList;\n while (!level.isEmpty()) {\n List<${pk.attrType}> parentIds = level.stream()\n .map(${ClassName}Entity::$str.getProperty($parentField))\n .filter(id -> id != null && !Objects.equals(id, ${treeRootId}) && !nodes.containsKey(id))\n .distinct()\n .toList();\n if (parentIds.isEmpty()) {\n break;\n }\n level = listByIds(parentIds);\n level.forEach(entity -> nodes.put(entity.$str.getProperty($pk.attrName)(), entity));\n }\n\n // 转换为TreeNode,仅包含命中节点及其祖先\n List<TreeNode<${pk.attrType}>> collect = nodes.values().stream().map(getNodeFunction()).toList();\n\n // 使用TreeUtil构建树形结构,根节点ID为0\n return TreeUtil.build(collect, ${treeRootId});\n }\n\n /**\n * 获取TreeNode转换函数\n * @return TreeNode转换函数\n */\n @NotNull\n private Function<${ClassName}Entity, TreeNode<${pk.attrType}>> getNodeFunction() {\n return entity -> {\n TreeNode<${pk.attrType}> node = new TreeNode<>();\n node.setId(entity.$str.getProperty($pk.attrName)());\n node.setName(entity.$str.getProperty($nameField)());\n node.setParentId(entity.$str.getProperty($parentField)() != null ? entity.$str.getProperty($parentField)() : ${treeRootId});\n\n // 扩展属性\n Map<String, Object> extra = new HashMap<>();\n#foreach($field in $fieldList)\n extra.put(${ClassName}Entity.Fields.${field.attrName}, entity.$str.getProperty($field.attrName)());\n#end\n node.setExtra(extra);\n return node;\n };\n }\n\n /**\n * 递归删除节点及其子节点\n * @param ids 要删除的节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public boolean removeBatchByIds(List<${pk.attrType}> ids) {\n if (CollUtil.isEmpty(ids)) {\n return true;\n }\n\n // 获取所有要删除的ID(包括子节点)\n List<${pk.attrType}> allDeleteIds = new ArrayList<>(ids);\n \n for (${pk.attrType} id : ids) {\n List<${pk.attrType}> childIds = getChildIdsRecursive(id);\n allDeleteIds.addAll(childIds);\n }\n\n // 去重\n allLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (26,'树形实体V202611','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','树形实体V202611','#set($hasOrg = false)\n#foreach($field in $fieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\npackage ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.spring.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.experimental.FieldNameConstants;\n#if($hasOrg)\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport ${package}.common.core.util.OrgRef;\nimport org.apache.fesod.sheet.annotation.ExcelIgnore;\nimport java.util.List;\n#end\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\n#if(!$import.startsWith(\'java.lang.\'))\nimport $import;\n#end\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n@FieldNameConstants\n#if($isTenant)\n@TenantTable\n#end\n@TableName(value = "${tableName}"#if($hasOrg), autoResultMap = true#end)\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = "${tableComment}")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.formType.startsWith(\'org-\'))\n @TableField(typeHandler = JacksonTypeHandler.class#if($field.autoFill == \'INSERT\' || $field.autoFill == \'INSERT_UPDATE\' || $field.autoFill == \'UPDATE\'), fill = FieldFill.${field.autoFill}#end)\n @ExcelIgnore\n#elseif($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description="$comment"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType.startsWith(\'org-\'))\n private List<OrgRef> $field.attrName;\n#elseif($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description="$comment 查询范围",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (27,'MapperV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV202611','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (28,'Mapper.xmlV202611','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV202611','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"#if($field.formType.startsWith(\'org-\')) typeHandler=\"com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler\"#end/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (29,'权限菜单V202611','${backendPath}/menu/${functionName}_menu.sql','权限菜单V202611','#set($permissionPrefix = \"${moduleName}_${functionName}\")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\n-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'${permissionPrefix}_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'${permissionPrefix}_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'${permissionPrefix}_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'${permissionPrefix}_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'${permissionPrefix}_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (30,'树形api.tsV202611','${frontendPath}/src/api/${moduleName}/${functionName}.ts','树形api.tsV202611','import request from \"/@/utils/request\"\n\n// ========== 树形表格CRUD接口 ==========\n\n/**\n * 获取树形列表数据\n * @param query - 查询参数对象\n * @returns Promise<树形数据>\n */\nexport function fetchTreeList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据(递归删除子节点)\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n * \n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (31,'树形表格V202611','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','树形表格V202611','## 有字典字段时才导入 useDict\n#set($hasDictField = false)\n#foreach($field in $queryList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n#foreach($field in $gridList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n<template>\n <div class="layout-padding">\n <div class="layout-padding-auto layout-padding-view">\n#if($queryList)\n <!-- 查询表单区域 -->\n <el-row v-show="showSearch">\n <el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))#set($queryKey = "${field.attrName}Keyword")#else#set($queryKey = $field.attrName)#end\n#if($field.queryFormType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${queryKey}">\n <el-select v-model="state.queryForm.${queryKey}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option \n :label="item.label" \n :value="item.value" \n v-for="(item, index) in ${field.fieldDict}" \n :key="index"\n />\n#else\n <el-option label="请选择" value="0" />\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${queryKey}">\n <el-date-picker \n type="date" \n placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" \n v-model="state.queryForm.${queryKey}"\n :value-format="dateStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetime\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${queryKey}">\n <el-date-picker\n type="datetime"\n placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"\n v-model="state.queryForm.${queryKey}"\n :value-format="dateTimeStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}Range">\n <el-date-picker\n type="daterange"\n range-separator="至"\n start-placeholder="开始日期"\n end-placeholder="结束日期"\n v-model="state.queryForm.${field.attrName}Range"\n :value-format="dateStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'datetimerange\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}Range">\n <el-date-picker\n type="datetimerange"\n range-separator="至"\n start-placeholder="开始时间"\n end-placeholder="结束时间"\n v-model="state.queryForm.${field.attrName}Range"\n :value-format="dateTimeStr"\n />\n </el-form-item>\n#elseif($field.formType == \'radio\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${queryKey}">\n <el-radio-group v-model="state.queryForm.${queryKey}">\n#if($field.fieldDict)\n <el-radio \n :label="item.value" \n v-for="(item, index) in ${field.fieldDict}" \n border \n :key="index"\n >\n {{ item.label }}\n </el-radio>\n#else\n <el-radio label="${field.fieldComment}" border>\n ${field.fieldComment}\n </el-radio>\n#end\n </el-radio-group>\n </el-form-item>\n#else\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#enLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (32,'树形表单V202611','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','树形表单V202611','#set($hasDuplicate = false)\n#foreach($field in $formList)\n#if($field.formValidator == \'duplicate\')\n#set($hasDuplicate = true)\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n#set($hasOrg = false)\n#foreach($field in $formList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\n<template>\n <el-dialog :title="form.${pk.attrName} ? \'编辑\' : \'新增\'" v-model="visible"\n :close-on-click-modal="false" draggable>\n <el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">\n <el-row :gutter="24">\n <!-- 父级节点选择 -->\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n <el-form-item label="父级节点" prop="${parentField}">\n <el-tree-select\n v-model="form.${parentField}"\n :data="parentNodes"\n :props="treeSelectProps"\n check-strictly\n :render-after-expand="false"\n placeholder="请选择父级节点"\n style="width: 100%"\n clearable\n filterable\n />\n </el-form-item>\n </el-col>\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName} && $field.attrName != ${parentField})\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n#if($field.formType.startsWith(\'org-\'))\n#set($orgType = $field.formType.replace(\'org-\', \'\').replace(\'-multiple\', \'\'))\n <el-form-item label="${field.fieldComment}" prop="${field.attrName}">\n <OrgSelect v-model="form.${field.attrName}" value-type="item" type="${orgType}" :multiple="#if($field.formType.endsWith(\'-multiple\'))true#else false#end" label="${field.fieldComment}" :disabled="loading" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'switch\')\n#switchValues($field)\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-switch :model-value="String(form.${field.attrName}) === String($switchOn)" @change="(checked) => (form.${field.attrName} = checked ? $switchOn : $switchOff)" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'text\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input type="textarea" v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-select v-model="form.${field.attrName}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option :value="item.value" :label="item.label" v-for="(item, index) in ${field.fieldDict}" :key="index"></el-option>\n#else\n <el-option label="请选择" value="0"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-radio-group v-model="form.${field.attrName}">\n#if($field.fieldDict)\n <el-radio :label="item.value" v-for="(item, index) in ${field.fieldDict}" border :key="index">{{ item.label }}</el-radio>\n#else\n <el-radio label="${field.fieldComment}" bordLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (33,'左树右表ControllerV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/controller/${ClassName}Controller.java','左树右表ControllerV202611','## 查询条件按需导入 StrUtil(字符串字段);Objects 用于树节点筛选,始终导入\n#set($needStrUtil = false)\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\') || ($field.formType != \'checkbox\' && $field.queryFormType != \'daterange\' && $field.queryFormType != \'datetimerange\' && $field.attrType == \'String\'))\n#set($needStrUtil = true)\n#end\n#end\n#set($hasOrgQuery = false)\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrgQuery = true)\n#end\n#end\n## 组织字段以 [{id,name}] JSON 存储,按 ${attrName}Keyword 对整列模糊查询,分页和导出共用\n#macro(orgKeywordParams)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), @RequestParam(required = false) String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordDecl)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), String ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordArgs)#foreach($field in $queryList)#if($field.formType.startsWith(\'org-\')), ${field.attrName}Keyword#end#end#end\n#macro(orgKeywordFilters)\n#if($hasOrgQuery)\n // 组织字段对整列 JSON 做 LIKE:关键字为数字或 id/name 时会误命中,需精确匹配时改用数据库 JSON 函数\n#end\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))\n wrapper.like(StrUtil.isNotBlank(${field.attrName}Keyword), ${ClassName}Entity::$str.getProperty($field.attrName), ${field.attrName}Keyword);\n#end\n#end\n#end\npackage ${package}.${moduleName}.controller;\n\n#if($needStrUtil)\nimport cn.hutool.core.util.StrUtil;\n#end\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${package}.common.core.util.R;\nimport ${package}.common.log.annotation.SysLog;\n#if($opensource)\nimport com.pig4cloud.plugin.excel.annotation.ResponseExcel;\nimport com.pig4cloud.plugin.excel.annotation.RequestExcel;\n#else\nimport ${package}.common.excel.annotation.ResponseExcel;\nimport ${package}.common.excel.annotation.RequestExcel;\n#end\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\nimport ${package}.${moduleName}.service.${ClassName}Service;\n\nimport io.swagger.v3.oas.annotations.security.SecurityRequirement;\n#if($isSpringBoot3)\nimport ${package}.common.security.annotation.HasPermission;\nimport org.springdoc.core.annotations.ParameterObject;\n#else\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springdoc.api.annotations.ParameterObject;\n#end\nimport org.springframework.http.HttpHeaders;\nimport io.swagger.v3.oas.annotations.tags.Tag;\nimport io.swagger.v3.oas.annotations.Operation;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($childFieldGetter = $str.getProperty($childField))\n@RestController\n@RequiredArgsConstructor\n@RequestMapping("/${functionName}" )\n@Tag(description = "${functionName}" , name = "${tableComment}管理" )\n@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)\npublic class ${ClassName}Controller {\n\n private final ${ClassName}Service ${className}Service;\n\n /**\n * 分页查询\n * @param page 分页对象\n * @param ${className} ${tableComment}\n * @return\n */\n @Operation(summary = "分页查询" , description = "分页查询" )\n @GetMapping("/page" )\n #if($isSpringBoot3)\n @HasPermission("$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view")\n #else\n @PreAuthorize("@pms.hasPermission(\'$str.lowerCase($moduleName)_$str.lowerCase($functionName)_view\')" )\n #end\n public R get${ClassName}Page(@ParameterObject Page page, @ParameterObject ${ClassName}Entity ${className}#orgKeywordParams()) {\n return R.ok(${className}Service.page(page, buildQueryWrapper(${className}#orgKeywordArgs())));\n }\n\n /**\n * 列表与导出共用的查询条件,保证导出结果与列表一致\n */\n private LambdaQueryWrapper<${ClassName}Entity> buildQueryWrapper(${ClassName}Entity ${className}#orgKeywordDecl()) {\n LambLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (34,'左树右表ServiceV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/${ClassName}Service.java','左树右表ServiceV202611','package ${package}.${moduleName}.service;\n\nimport cn.hutool.core.lang.tree.Tree;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.spring.service.IService;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\n\nimport java.util.List;\n\n/**\n * ${tableComment} Service接口\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\npublic interface ${ClassName}Service extends IService<${ClassName}Entity> {\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n List<Tree<${childPkField.attrType}>> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper);\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName});\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n Boolean saveTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n Boolean updateTree(${ChildClassName}Entity ${childClassName});\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n Boolean removeTreeByIds(${childPkField.attrType}[] ids);\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (35,'左树右表ServiceImplV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/service/impl/${ClassName}ServiceImpl.java','左树右表ServiceImplV202611','package ${package}.${moduleName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport cn.hutool.core.lang.tree.Tree;\nimport cn.hutool.core.lang.tree.TreeNode;\nimport cn.hutool.core.lang.tree.TreeUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport ${entityClassName};\nimport ${package}.${moduleName}.mapper.${ChildClassName}Mapper;\nimport ${package}.${moduleName}.mapper.${ClassName}Mapper;\nimport ${package}.${moduleName}.service.${ClassName}Service;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.function.Function;\n\n/**\n * ${tableComment} Service实现类\n *\n * @author ${author}\n * @date ${datetime}\n */\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#set($childPkGetter = $str.getProperty($childPkField.attrName))\n#break\n#end\n#end\n#set($parentGetter = $str.getProperty($parentField))\n#set($childFieldGetter = $str.getProperty($childField))\n#if($childPkField.attrType == \'Long\')\n#set($treeRootId = \'0L\')\n#elseif($childPkField.attrType == \'String\')\n#set($treeRootId = \'\"0\"\')\n#else\n#set($treeRootId = \'0\')\n#end\n@Service\n@RequiredArgsConstructor\npublic class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}Entity> implements ${ClassName}Service {\n\n private final ${ChildClassName}Mapper ${childClassName}Mapper;\n\n /**\n * 构建树形结构数据\n * @param wrapper 查询条件\n * @return 树形结构数据\n */\n @Override\n public List<Tree<${childPkField.attrType}>> buildTree(LambdaQueryWrapper<${ChildClassName}Entity> wrapper) {\n List<${ChildClassName}Entity> allList = ${childClassName}Mapper.selectList(wrapper);\n\n if (CollUtil.isEmpty(allList)) {\n return new ArrayList<>();\n }\n\n List<TreeNode<${childPkField.attrType}>> collect = allList.stream().map(getNodeFunction()).toList();\n return TreeUtil.build(collect, ${treeRootId});\n }\n\n /**\n * 获取树节点详情\n * @param ${childClassName} 树节点查询对象\n * @return 树节点详情列表\n */\n @Override\n public List<${ChildClassName}Entity> getTreeDetails(${ChildClassName}Entity ${childClassName}) {\n return ${childClassName}Mapper.selectList(Wrappers.query(${childClassName}));\n }\n\n /**\n * 保存树节点\n * @param ${childClassName} 树节点对象\n * @return 保存结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean saveTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(null, ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.insert(${childClassName}) > 0;\n }\n\n /**\n * 更新树节点\n * @param ${childClassName} 树节点对象\n * @return 更新结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean updateTree(${ChildClassName}Entity ${childClassName}) {\n ${childClassName}.$str.setProperty($parentField)(validateParentRelation(${childClassName}.$str.getProperty($childPkField.attrName)(), ${childClassName}.$str.getProperty($parentField)()));\n return ${childClassName}Mapper.updateById(${childClassName}) > 0;\n }\n\n /**\n * 批量删除树节点\n * @param ids 树节点ID列表\n * @return 删除结果\n */\n @Override\n @Transactional(rollbackFor = Exception.class)\n public Boolean removeTreeByIds(${childPkField.attrType}[] ids) {\n if (ArrayUtil.isEmpty(ids)) {\n return Boolean.TRUE;\n }\n\n for (${childPkField.attrType} id : ids) {\n if (hasChildNodes(id)) {\n throw new RuntimeException(\"当前节点存在子节点,不能删除\");\n }\n if (hasMainRecords(id)) {\n throw new RuntimeException(\"当前节点已关联主表数据,不能删除\");\n }\n }\n\n return ${childClassNameLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (36,'实体V202611','${entityPath}/src/main/java/${entityPackagePath}/${ClassName}Entity.java','实体V202611','#set($hasOrg = false)\n#foreach($field in $fieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\npackage ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.spring.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($hasOrg)\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport ${package}.common.core.util.OrgRef;\nimport org.apache.fesod.sheet.annotation.ExcelIgnore;\nimport java.util.List;\n#end\n#if($isTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\n#foreach($import in $importList)\n#if(!$import.startsWith(\'java.lang.\'))\nimport $import;\n#end\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isTenant)\n@TenantTable\n#end\n@TableName(value = \"${tableName}\"#if($hasOrg), autoResultMap = true#end)\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${tableComment}\")\npublic class ${ClassName}Entity extends Model<${ClassName}Entity> {\n\n#foreach ($field in $fieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.formType.startsWith(\'org-\'))\n @TableField(typeHandler = JacksonTypeHandler.class#if($field.autoFill == \'INSERT\' || $field.autoFill == \'INSERT_UPDATE\' || $field.autoFill == \'UPDATE\'), fill = FieldFill.${field.autoFill}#end)\n @ExcelIgnore\n#elseif($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType.startsWith(\'org-\'))\n private List<OrgRef> $field.attrName;\n#elseif($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#elseif($field.formType == \'daterange\' || $field.formType == \'datetimerange\')\n private String[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end\n#end\n#foreach ($field in $queryList)\n#if($field.queryFormType == \'daterange\' || $field.queryFormType == \'datetimerange\')\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n\n /**\n * $comment 查询范围\n */\n @TableField(exist = false)\n @Schema(description=\"$comment 查询范围\",hidden=true)\n private String[] ${field.attrName}Range;\n#end\n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (37,'MapperV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ClassName}Mapper.java','MapperV202611','package ${package}.${moduleName}.mapper;\n\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport ${entityClassName};\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ClassName}Mapper extends BaseMapper<${ClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (38,'Mapper.xmlV202611','${backendPath}/src/main/resources/mapper/${ClassName}Mapper.xml','Mapper.xmlV202611','<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n<mapper namespace=\"${package}.${moduleName}.mapper.${ClassName}Mapper\">\n\n <resultMap id=\"${className}Map\" type=\"${entityClassName}\">\n#foreach ($field in $fieldList)\n #if($field.primaryPk == \'1\')\n <id property=\"$field.attrName\" column=\"$field.fieldName\"/>\n #else\n <result property=\"$field.attrName\" column=\"$field.fieldName\"#if($field.formType.startsWith(\'org-\')) typeHandler=\"com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler\"#end/>\n #end\n#end\n </resultMap>\n</mapper>\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (39,'子实体V202611','${entityPath}/src/main/java/${entityPackagePath}/${ChildClassName}Entity.java','子实体V202611','#set($hasOrg = false)\n#foreach($field in $childFieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\npackage ${entityPackage};\n\nimport com.baomidou.mybatisplus.annotation.*;\nimport com.baomidou.mybatisplus.spring.activerecord.Model;\nimport io.swagger.v3.oas.annotations.media.Schema;\n#if($isChildTenant)\nimport ${package}.common.core.util.TenantTable;\n#end\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n#if($hasOrg)\nimport com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;\nimport ${package}.common.core.util.OrgRef;\nimport java.util.List;\n#end\n#set($childImports = [])\n#foreach($field in $childFieldList)\n#if($field.packageName && !$field.packageName.startsWith(\'java.lang.\') && !$childImports.contains($field.packageName))\n#set($void = $childImports.add($field.packageName))\n#end\n#end\n#foreach($import in $childImports)\nimport $import;\n#end\n\n/**\n * ${tableComment}\n *\n * @author ${author}\n * @date ${datetime}\n */\n@Data\n#if($isChildTenant)\n@TenantTable\n#end\n@TableName(value = \"${childTableName}\"#if($hasOrg), autoResultMap = true#end)\n@EqualsAndHashCode(callSuper = true)\n@Schema(description = \"${childTableName}\")\npublic class ${ChildClassName}Entity extends Model<${ChildClassName}Entity> {\n\n#foreach ($field in $childFieldList)\n#if(${field.fieldComment})#set($comment=${field.fieldComment})#else #set($comment=${field.attrName})#end\n /**\n * $comment\n */\n#if($field.primaryPk == \'1\')\n @TableId(type = IdType.ASSIGN_ID)\n#end\n#if($field.formType.startsWith(\'org-\'))\n @TableField(typeHandler = JacksonTypeHandler.class#if($field.autoFill == \'INSERT\' || $field.autoFill == \'INSERT_UPDATE\' || $field.autoFill == \'UPDATE\'), fill = FieldFill.${field.autoFill}#end)\n#elseif($field.autoFill == \'INSERT\')\n @TableField(fill = FieldFill.INSERT)\n#elseif($field.autoFill == \'INSERT_UPDATE\')\n @TableField(fill = FieldFill.INSERT_UPDATE)\n#elseif($field.autoFill == \'UPDATE\')\n @TableField(fill = FieldFill.UPDATE)\n#end\n#if($field.fieldName == \'del_flag\')\n @TableLogic\n @TableField(fill = FieldFill.INSERT)\n#end\n @Schema(description=\"$comment\"#if($field.hidden),hidden=$field.hidden#end)\n#if($field.formType.startsWith(\'org-\'))\n private List<OrgRef> $field.attrName;\n#elseif($field.formType == \'checkbox\')\n private ${field.attrType}[] $field.attrName;\n#else\n private $field.attrType $field.attrName;\n#end \n#end\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (40,'子MapperV202611','${backendPath}/src/main/java/${packagePath}/${moduleName}/mapper/${ChildClassName}Mapper.java','子MapperV202611','package ${package}.${moduleName}.mapper;\n\nimport ${package}.common.data.datascope.PigxBaseMapper;\nimport ${entityPackage}.${ChildClassName}Entity;\nimport org.apache.ibatis.annotations.Mapper;\n\n@Mapper\npublic interface ${ChildClassName}Mapper extends PigxBaseMapper<${ChildClassName}Entity> {\n\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (41,'权限菜单V202611','${backendPath}/menu/${functionName}_menu.sql','权限菜单V202611','#set($permissionPrefix = \"${moduleName}_${functionName}\")\n#set($permissionPrefix = $permissionPrefix.toLowerCase())\n-- 该脚本不要直接执行, 注意维护菜单的父节点ID 默认 父节点-1 , #if(!$opensource)默认租户 1#end\n#set($menuId=${dateTool.getSystemTime()})\n\n-- 菜单SQL\ninsert into sys_menu ( menu_id,parent_id, path, permission, menu_type, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${menuId}, \'-1\', \'/${moduleName}/${functionName}/index\', \'\', \'0\', \'icon-bangzhushouji\', \'0\', null , \'8\', null , \'${tableComment}管理\'#if(!$opensource), 1#end);\n\n-- 菜单对应按钮SQL\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,1)},${menuId}, \'${permissionPrefix}_view\', \'1\', null, \'1\', \'0\', null, \'0\', null, \'${tableComment}查看\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,2)},${menuId}, \'${permissionPrefix}_add\', \'1\', null, \'1\', \'0\', null, \'1\', null, \'${tableComment}新增\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,3)},${menuId}, \'${permissionPrefix}_edit\', \'1\', null, \'1\', \'0\', null, \'2\', null, \'${tableComment}修改\'#if(!$opensource), 1#end);\n\ninsert into sys_menu (menu_id, parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,4)},${menuId}, \'${permissionPrefix}_del\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'${tableComment}删除\'#if(!$opensource), 1#end);\n\ninsert into sys_menu ( menu_id,parent_id, permission, menu_type, path, icon, del_flag, create_time, sort_order, update_time, name#if(!$opensource), tenant_id#end)\nvalues (${math.add($menuId,5)},${menuId}, \'${permissionPrefix}_export\', \'1\', null, \'1\', \'0\', null, \'3\', null, \'导入导出\'#if(!$opensource), 1#end);\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (42,'左树右表api.tsV202611','${frontendPath}/src/api/${moduleName}/${functionName}.ts','左树右表api.tsV202611','import request from \"/@/utils/request\"\n\n// ========== 基础CRUD接口 ==========\n\n/**\n * 分页查询列表数据\n * @param query - 查询参数对象\n * @returns Promise<分页数据>\n */\nexport function fetchList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/page\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 新增数据\n * @param obj - 要新增的数据对象\n * @returns Promise<boolean> - 操作结果\n */\nexport function addObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 获取详情数据\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<数据详情>\n */\nexport function getObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 批量删除数据\n * @param ids - 要删除的ID数组\n * @returns Promise<操作结果>\n */\nexport function delObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'delete\',\n data: ids\n })\n}\n\n/**\n * 更新数据\n * @param obj - 要更新的数据对象\n * @returns Promise<操作结果>\n */\nexport function putObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}\',\n method: \'put\',\n data: obj\n })\n}\n\n// ========== 树形CRUD接口 ==========\n\n/**\n * 获取树形列表数据\n * @param query - 查询参数对象\n * @returns Promise<树形数据>\n */\nexport function fetchTreeList(query?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'get\',\n params: query\n })\n}\n\n/**\n * 获取树节点详情\n * @param obj - 查询参数对象(包含ID等)\n * @returns Promise<树节点详情>\n */\nexport function getTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree/details\',\n method: \'get\',\n params: obj\n })\n}\n\n/**\n * 新增树节点\n * @param obj - 要新增的树节点对象\n * @returns Promise<操作结果>\n */\nexport function addTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'post\',\n data: obj\n })\n}\n\n/**\n * 更新树节点\n * @param obj - 要更新的树节点对象\n * @returns Promise<操作结果>\n */\nexport function putTreeObj(obj?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'put\',\n data: obj\n })\n}\n\n/**\n * 删除树节点\n * @param ids - 要删除的树节点ID数组\n * @returns Promise<操作结果>\n */\nexport function delTreeObjs(ids?: Object) {\n return request({\n url: \'/${moduleName}/${functionName}/tree\',\n method: \'delete\',\n data: ids\n })\n}\n\n// ========== 工具函数 ==========\n\n/**\n * 验证字段值唯一性\n * @param rule - 验证规则对象\n * @param value - 要验证的值\n * @param callback - 验证回调函数\n * @param isEdit - 是否为编辑模式\n *\n * @example\n * // 在表单验证规则中使用\n * fieldName: [\n * {\n * validator: (rule, value, callback) => {\n * validateExist(rule, value, callback, form.${pk.attrName} !== \'\');\n * },\n * trigger: \'blur\',\n * },\n * ]\n */\nexport function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {\n // 编辑模式下跳过验证\n if (isEdit) {\n return callback();\n }\n\n // 查询是否存在相同值\n getObj({ [rule.field]: value }).then((response) => {\n const result = response.data;\n if (result !== null && result.length > 0) {\n callback(new Error(\'数据已经存在\'));\n } else {\n callback();\n }\n });\n}\n','2026-04-08 17:38:24','2026-04-08 17:38:24','0',1,'admin','admin');
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (43,'左树右表表格V202611','${frontendPath}/src/views/${moduleName}/${functionName}/index.vue','左树右表表格V202611','## 有字典字段时才导入 useDict\n#set($hasDictField = false)\n#foreach($field in $queryList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n#foreach($field in $gridList)\n#if($field.fieldDict)\n#set($hasDictField = true)\n#end\n#end\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n<template>\n <div class="layout-padding">\n <splitpanes>\n <pane size="15">\n <div class="layout-padding-auto layout-padding-view">\n <el-scrollbar>\n <div class="mb8" style="display: flex; align-items: center; justify-content: space-between;">\n <span style="font-weight: 500;">左侧节点</span>\n <div>\n <el-tooltip :content="currentTreeId ? \'新增子节点\' : \'新增根节点\'" placement="top">\n <el-button link type="primary" icon="FolderAdd" @click="handleAddTree" />\n </el-tooltip>\n </div>\n </div>\n <el-tree\n ref="treeRef"\n :data="treeData"\n :props="treeProps"\n node-key="${childPkField.attrName}"\n highlight-current\n default-expand-all\n :expand-on-click-node="false"\n @node-click="handleTreeNodeClick"\n >\n <template #default="{ node, data }">\n <span style="flex: 1; display: flex; align-items: center; justify-content: space-between;">\n <span>{{ node.label }}</span>\n <span v-if="currentTreeId === data.${childPkField.attrName}" style="margin-left: 8px;">\n <el-tooltip content="编辑" placement="top">\n <el-button link type="primary" icon="Edit" size="small" @click.stop="handleEditTree" />\n </el-tooltip>\n <el-tooltip content="删除" placement="top">\n <el-button link type="danger" icon="Delete" size="small" @click.stop="handleDeleteTree" />\n </el-tooltip>\n </span>\n </span>\n </template>\n </el-tree>\n </el-scrollbar>\n </div>\n </pane>\n <pane class="ml8">\n <div class="layout-padding-auto layout-padding-view">\n#if($queryList)\n <el-row v-show="showSearch">\n <el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">\n#foreach($field in $queryList)\n#if($field.formType.startsWith(\'org-\'))#set($queryKey = "${field.attrName}Keyword")#else#set($queryKey = $field.attrName)#end\n#if($field.attrName != ${childField})\n#if($field.queryFormType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${queryKey}">\n <el-select v-model="state.queryForm.${queryKey}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option \n :label="item.label" \n :value="item.value" \n v-for="(item, index) in ${field.fieldDict}" \n :key="index"\n />\n#else\n <el-option label="请选择" value="0" />\n#end\n </el-select>\n </el-form-item>\n#elseif($field.queryFormType == \'date\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${queryKey}">\n <el-date-picker\n type="date"\n placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"\n v-model="state.queryForm.${queryKey}"\n :value-format="dateStr"\n />\n </el-form-item>\n#elseif($field.queryFormType == \'daterange\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}Range">\n <el-date-pickerLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (44,'左树右表树表单V202611','${frontendPath}/src/views/${moduleName}/${functionName}/tree-form.vue','左树右表树表单V202611','#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($fieldDict=[])\n#foreach($field in $childFieldList)\n#if($field.fieldDict)\n#set($void=$fieldDict.add($field.fieldDict))\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n#set($hasOrg = false)\n#foreach($field in $childFieldList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\n<template>\n <el-dialog :title="form.${childPkField.attrName} ? \'编辑\' : \'新增\'" v-model="visible"\n :close-on-click-modal="false" draggable>\n <el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="90px" v-loading="loading">\n <el-row :gutter="24">\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n <el-form-item label="父级节点" prop="${parentField}">\n <el-tree-select\n v-model="form.${parentField}"\n :data="parentNodes"\n :props="treeSelectProps"\n check-strictly\n :render-after-expand="false"\n placeholder="请选择父级节点"\n style="width: 100%"\n clearable\n filterable\n />\n </el-form-item>\n </el-col>\n#foreach($field in $childFieldList)\n#if($field.attrName != ${childPkField.attrName} && $field.attrName != ${parentField})\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n#if($field.formType.startsWith(\'org-\'))\n#set($orgType = $field.formType.replace(\'org-\', \'\').replace(\'-multiple\', \'\'))\n <el-form-item label="${field.fieldComment}" prop="${field.attrName}">\n <OrgSelect v-model="form.${field.attrName}" value-type="item" type="${orgType}" :multiple="#if($field.formType.endsWith(\'-multiple\'))true#else false#end" label="${field.fieldComment}" :disabled="loading" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'switch\')\n#switchValues($field)\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-switch :model-value="String(form.${field.attrName}) === String($switchOn)" @change="(checked) => (form.${field.attrName} = checked ? $switchOn : $switchOff)" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'text\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input type="textarea" v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-select v-model="form.${field.attrName}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option :value="item.value" :label="item.label" v-for="(item, index) in ${field.fieldDict}" :key="index"></el-option>\n#else\n <el-option label="请选择" value="0"></el-option>\n#end\n </el-select>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'radio\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-radio-group v-model="form.${field.attrName}">\n#if($field.fieldDict)\n <el-radio :label="item.value" v-for="(item, index) in ${field.fieldDicLine truncated
INSERT INTO `gen_template` (`id`, `template_name`, `generator_path`, `template_desc`, `template_code`, `create_time`, `update_time`, `del_flag`, `tenant_id`, `create_by`, `update_by`) VALUES (45,'左树右表主表单V202611','${frontendPath}/src/views/${moduleName}/${functionName}/form.vue','左树右表主表单V202611','#set($hasDuplicate = false)\n#foreach($field in $formList)\n#if($field.attrName != ${childField} && $field.formValidator == \'duplicate\')\n#set($hasDuplicate = true)\n#end\n#end\n#foreach($field in $childFieldList)\n#if($field.primaryPk == \'1\')\n#set($childPkField = $field)\n#break\n#end\n#end\n#set($relationFieldLabel = $childField)\n#foreach($field in $fieldList)\n#if($field.attrName == $childField)\n#if($field.fieldComment)\n#set($relationFieldLabel = $field.fieldComment)\n#else\n#set($relationFieldLabel = $field.attrName)\n#end\n#break\n#end\n#end\n#set($fieldDict=[])\n#foreach($field in $formList)\n#if($field.fieldDict)\n#set($void=$fieldDict.add($field.fieldDict))\n#end\n#end\n## 开关:按属性类型决定开/关的值,Boolean 用 true/false,整型用 1/0,其余用 \'1\'/\'0\'\n#macro(switchValues $f)\n#if($f.attrType == \'Boolean\')\n#set($switchOn = \'true\')\n#set($switchOff = \'false\')\n#elseif($f.attrType == \'Integer\' || $f.attrType == \'Long\' || $f.attrType == \'Short\')\n#set($switchOn = \'1\')\n#set($switchOff = \'0\')\n#else\n#set($switchOn = "\'1\'")\n#set($switchOff = "\'0\'")\n#end\n#end\n#set($hasOrg = false)\n#foreach($field in $formList)\n#if($field.formType.startsWith(\'org-\'))\n#set($hasOrg = true)\n#end\n#end\n<template>\n <el-dialog :title="form.${pk.attrName} ? \'编辑\' : \'新增\'" v-model="visible"\n :close-on-click-modal="false" draggable>\n <el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="90px" v-loading="loading">\n <el-row :gutter="24">\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n <el-form-item label="${relationFieldLabel}" prop="${childField}">\n <el-tree-select\n v-model="form.${childField}"\n :data="treeNodes"\n :props="treeSelectProps"\n check-strictly\n :render-after-expand="false"\n placeholder="请选择${relationFieldLabel}"\n style="width: 100%"\n clearable\n filterable\n />\n </el-form-item>\n </el-col>\n#foreach($field in $formList)\n#if($field.attrName != ${pk.attrName} && $field.attrName != ${childField})\n#if($formLayout == 1)\n <el-col :span="24" class="mb20">\n#elseif($formLayout == 2)\n <el-col :span="12" class="mb20">\n#end\n#if($field.formType.startsWith(\'org-\'))\n#set($orgType = $field.formType.replace(\'org-\', \'\').replace(\'-multiple\', \'\'))\n <el-form-item label="${field.fieldComment}" prop="${field.attrName}">\n <OrgSelect v-model="form.${field.attrName}" value-type="item" type="${orgType}" :multiple="#if($field.formType.endsWith(\'-multiple\'))true#else false#end" label="${field.fieldComment}" :disabled="loading" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'switch\')\n#switchValues($field)\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-switch :model-value="String(form.${field.attrName}) === String($switchOn)" @change="(checked) => (form.${field.attrName} = checked ? $switchOn : $switchOff)" />\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'text\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'textarea\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-input type="textarea" v-model="form.${field.attrName}" placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"/>\n </el-form-item>\n </el-col>\n#elseif($field.formType == \'select\')\n <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">\n <el-select v-model="form.${field.attrName}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">\n#if($field.fieldDict)\n <el-option :value="item.value" :label="item.label" v-for="(item, index) in ${field.fieldDict}" :key="index"></el-option>\n#else\n <el-option label="请选择" value="0"></el-option>\n#end\n Line truncated
COMMIT;
-- ----------------------------
+29
View File
@@ -3,6 +3,35 @@ USE pigxx_bi;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for chat2bi_table_meta
-- ----------------------------
DROP TABLE IF EXISTS `chat2bi_table_meta`;
CREATE TABLE `chat2bi_table_meta` (
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键',
`db_source_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '数据源来源表标识, 如: db_source / api_source / file_source / system_source',
`db_source_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '对应来源表的主键ID',
`schema_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'Schema名',
`table_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '表名',
`source_comment` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '表注释-源(同步写入, 每次同步覆盖)',
`table_comment` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '表注释-用户(用户手动填写, 同步不覆盖)',
`columns_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '列定义[{name,type,source_comment,comment,is_primary,sample_values,nulltable}]',
`relations_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '关联关系[{column,ref_table,ref_column,type}]',
`is_enabled` tinyint(4) NOT NULL DEFAULT 1 COMMENT '对LLM可见: 0=否 1=是',
`synced_time` datetime NULL DEFAULT NULL COMMENT '最近同步时间',
`create_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '更新人',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
`tenant_id` int(11) NULL DEFAULT NULL COMMENT '多租户标识',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_source_table`(`db_source_type`, `db_source_id`, `schema_name`, `table_name`) USING BTREE,
INDEX `idx_source`(`db_source_type`, `db_source_id`) USING BTREE,
INDEX `idx_table_name`(`table_name`) USING BTREE,
INDEX `idx_table_name_comment`(`table_name`, `source_comment`(320), `table_comment`(320)) USING BTREE,
INDEX `idx_table_comment`(`table_comment`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '表元数据(同步+手动维护, 支持多数据源表)' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Table structure for huiyuan_age
-- ----------------------------
+1 -1
View File
@@ -7,7 +7,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-app-server</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-app-server-api</artifactId>
@@ -1,7 +1,7 @@
package com.pig4cloud.pigx.app.api.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.spring.activerecord.Model;
import com.pig4cloud.pigx.common.core.util.TenantTable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -1,7 +1,7 @@
package com.pig4cloud.pigx.app.api.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.spring.activerecord.Model;
import com.pig4cloud.pigx.common.core.util.TenantTable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -1,7 +1,7 @@
package com.pig4cloud.pigx.app.api.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.spring.activerecord.Model;
import com.pig4cloud.pigx.common.core.util.TenantTable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -1,7 +1,7 @@
package com.pig4cloud.pigx.app.api.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.spring.activerecord.Model;
import com.pig4cloud.pigx.common.core.util.TenantTable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -1,7 +1,7 @@
package com.pig4cloud.pigx.app.api.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.spring.activerecord.Model;
import com.pig4cloud.pigx.common.core.util.TenantTable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -1,7 +1,7 @@
package com.pig4cloud.pigx.app.api.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.spring.activerecord.Model;
import com.pig4cloud.pigx.common.core.util.TenantTable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -1,7 +1,7 @@
package com.pig4cloud.pigx.app.api.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.spring.activerecord.Model;
import com.fasterxml.jackson.annotation.JsonRawValue;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.pig4cloud.pigx.common.core.util.TenantTable;
+1 -1
View File
@@ -7,7 +7,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-app-server</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-app-server-biz</artifactId>
@@ -51,6 +51,7 @@ public class AppArticleCategoryController {
LambdaQueryWrapper<AppArticleCategoryEntity> wrapper = Wrappers.lambdaQuery();
wrapper.like(StrUtil.isNotBlank(appArticleCategory.getName()), AppArticleCategoryEntity::getName,
appArticleCategory.getName());
wrapper.orderByAsc(AppArticleCategoryEntity::getSort, AppArticleCategoryEntity::getId);
return R.ok(appArticleCategoryService.page(page, wrapper));
}
@@ -74,7 +75,8 @@ public class AppArticleCategoryController {
@Inner(value = false)
@GetMapping("/list")
public R list() {
return R.ok(appArticleCategoryService.list());
return R.ok(appArticleCategoryService.list(Wrappers.<AppArticleCategoryEntity>lambdaQuery()
.orderByAsc(AppArticleCategoryEntity::getSort, AppArticleCategoryEntity::getId)));
}
/**
@@ -103,6 +105,24 @@ public class AppArticleCategoryController {
return R.ok(appArticleCategoryService.updateById(appArticleCategory));
}
/**
* 按传入 ID 顺序调整文章分类,保留未选中记录在排序序列中的位置。
* <p>
* 以当前租户内未删除记录的排序值、主键升序为基准,仅重排选中记录占据的位置, 再将全部记录的排序值从 1 起连续重编号。
* @param ids 按目标顺序排列的文章分类 ID;不可为 null 或空,不可包含 null、重复或不可用的 ID
* @return 排序保存成功的响应;校验或更新失败时抛出异常,由统一异常处理器转换响应
* @throws IllegalArgumentException ID 列表为空、包含 null 或重复值,或指定记录已删除、不属于当前租户 或不存在
* @throws IllegalStateException 批量更新返回失败
*/
@Operation(summary = "拖拽排序", description = "按当前列表顺序保存排序")
@SysLog("文章分类排序")
@PutMapping("/sort")
@HasPermission("app_appArticleCategory_edit")
public R sort(@RequestBody List<Long> ids) {
appArticleCategoryService.sort(ids);
return R.ok();
}
/**
* 通过id删除文章分类表
* @param ids id列表
@@ -36,13 +36,12 @@ public class AppArticleController {
private final AppArticleService appArticleService;
/**
* 分页查询文章资讯。
* <p>
* 返回文章列表时会关联分类名称,供 App 资讯列表和后台管理复用。
*
* 分页查询文章资讯。
* <p>
* 返回文章列表时会关联分类名称,供 App 资讯列表和后台管理复用。
* @param page 分页对象
* @param appArticle 文章资讯
* @return 文章分页结果
* @return 文章分页结果
*/
@Operation(summary = "分页查询", description = "分页查询")
@Inner(value = false)
@@ -52,13 +51,12 @@ public class AppArticleController {
}
/**
* 查询文章详情。
* <p>
* 会递增访问次数;如果传入 userId,则额外返回该用户是否收藏当前文章。
*
* @param id 文章 ID
* @param userId 用户 ID,可为空
* @return 文章详情
* 查询文章详情。
* <p>
* 会递增访问次数;如果传入 userId,则额外返回该用户是否收藏当前文章。
* @param id 文章 ID
* @param userId 用户 ID,可为空
* @return 文章详情
*/
@Inner(value = false)
@Operation(summary = "通过id查询", description = "通过id查询")
@@ -93,6 +91,24 @@ public class AppArticleController {
return R.ok(appArticleService.updateById(appArticle));
}
/**
* 按传入 ID 顺序调整文章,保留未选中记录在排序序列中的位置。
* <p>
* 以当前租户内未删除记录的排序值、主键升序为基准,仅重排选中记录占据的位置, 再将全部记录的排序值从 1 起连续重编号。
* @param ids 按目标顺序排列的文章 ID;不可为 null 或空,不可包含 null、重复或不可用的 ID
* @return 排序保存成功的响应;校验或更新失败时抛出异常,由统一异常处理器转换响应
* @throws IllegalArgumentException ID 列表为空、包含 null 或重复值,或指定记录已删除、不属于当前租户 或不存在
* @throws IllegalStateException 批量更新返回失败
*/
@Operation(summary = "拖拽排序", description = "按当前列表顺序保存排序")
@SysLog("文章资讯排序")
@PutMapping("/sort")
@HasPermission("app_appArticle_edit")
public R sort(@RequestBody List<Long> ids) {
appArticleService.sort(ids);
return R.ok();
}
/**
* 通过id删除文章资讯
* @param ids id列表
@@ -1,11 +1,23 @@
package com.pig4cloud.pigx.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.spring.service.IService;
import com.pig4cloud.pigx.app.api.entity.AppArticleCategoryEntity;
import java.util.List;
/**
* 文章分类基础服务。
*/
public interface AppArticleCategoryService extends IService<AppArticleCategoryEntity> {
/**
* 按传入 ID 顺序调整文章分类,保留未选中记录在排序序列中的位置。
* <p>
* 以当前租户内未删除记录的排序值、主键升序为基准,仅重排选中记录占据的位置, 再将全部记录的排序值从 1 起连续重编号。
* @param ids 按目标顺序排列的文章分类 ID;不可为 null 或空,不可包含 null、重复或不可用的 ID
* @throws IllegalArgumentException ID 列表为空、包含 null 或重复值,或指定记录已删除、不属于当前租户 或不存在
* @throws IllegalStateException 批量更新返回失败
*/
void sort(List<Long> ids);
}
@@ -1,11 +1,26 @@
package com.pig4cloud.pigx.app.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.spring.service.IService;
import com.pig4cloud.pigx.app.api.entity.AppArticleEntity;
import java.util.List;
/**
* 文章查询与排序服务,供管理端和 App 文章接口调用。
*/
public interface AppArticleService extends IService<AppArticleEntity> {
/**
* 按传入 ID 顺序调整文章,保留未选中记录在排序序列中的位置。
* <p>
* 以当前租户内未删除记录的排序值、主键升序为基准,仅重排选中记录占据的位置, 再将全部记录的排序值从 1 起连续重编号。
* @param ids 按目标顺序排列的文章 ID;不可为 null 或空,不可包含 null、重复或不可用的 ID
* @throws IllegalArgumentException ID 列表为空、包含 null 或重复值,或指定记录已删除、不属于当前租户 或不存在
* @throws IllegalStateException 批量更新返回失败
*/
void sort(List<Long> ids);
/**
* 获取文章并使阅读数+1
* @param id id
@@ -1,7 +1,7 @@
package com.pig4cloud.pigx.app.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.spring.service.IService;
import com.pig4cloud.pigx.app.api.entity.AppContactsEntity;
/**
@@ -1,6 +1,6 @@
package com.pig4cloud.pigx.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.spring.service.IService;
import com.pig4cloud.pigx.app.api.entity.AppPageEntity;
/**
@@ -1,6 +1,6 @@
package com.pig4cloud.pigx.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.spring.service.IService;
import com.pig4cloud.pigx.app.api.dto.AppRoleTabbarDTO;
import com.pig4cloud.pigx.app.api.entity.AppRoleTabbar;
@@ -1,6 +1,6 @@
package com.pig4cloud.pigx.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.spring.service.IService;
import com.pig4cloud.pigx.app.api.entity.AppTabbarEntity;
/**
@@ -1,10 +1,18 @@
package com.pig4cloud.pigx.app.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
import com.pig4cloud.pigx.app.api.entity.AppArticleCategoryEntity;
import com.pig4cloud.pigx.app.mapper.AppArticleCategoryMapper;
import com.pig4cloud.pigx.app.service.AppArticleCategoryService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 文章分类表
@@ -16,4 +24,37 @@ import org.springframework.stereotype.Service;
public class AppArticleCategoryServiceImpl extends ServiceImpl<AppArticleCategoryMapper, AppArticleCategoryEntity>
implements AppArticleCategoryService {
/**
* 按传入 ID 顺序调整文章分类,保留未选中记录在排序序列中的位置。
* <p>
* 以当前租户内未删除记录的排序值、主键升序为基准,仅重排选中记录占据的位置, 再将全部记录的排序值从 1 起连续重编号。
* @param ids 按目标顺序排列的文章分类 ID;不可为 null 或空,不可包含 null、重复或不可用的 ID
* @throws IllegalArgumentException ID 列表为空、包含 null 或重复值,或指定记录已删除、不属于当前租户 或不存在
* @throws IllegalStateException 批量更新返回失败
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void sort(List<Long> ids) {
Assert.notEmpty(ids, "排序记录不能为空");
Set<Long> selected = new HashSet<>(ids);
Assert.isTrue(!selected.contains(null) && selected.size() == ids.size(), "排序记录无效");
// 锁定统一顺序,避免重复排序值和分页边界导致拖动后顺序丢失。
List<AppArticleCategoryEntity> records = list(Wrappers.<AppArticleCategoryEntity>lambdaQuery()
.orderByAsc(AppArticleCategoryEntity::getSort, AppArticleCategoryEntity::getId)
.last("FOR UPDATE"));
long matched = records.stream().filter(row -> selected.contains(row.getId())).count();
Assert.isTrue(matched == ids.size(), "记录已变更,请刷新后重试");
List<AppArticleCategoryEntity> updates = new ArrayList<>();
int next = 0;
for (int i = 0; i < records.size(); i++) {
AppArticleCategoryEntity current = records.get(i);
Long id = selected.contains(current.getId()) ? ids.get(next++) : current.getId();
AppArticleCategoryEntity update = new AppArticleCategoryEntity();
update.setId(id);
update.setSort(i + 1);
updates.add(update);
}
Assert.state(updateBatchById(updates), "排序保存失败");
}
}
@@ -1,7 +1,7 @@
package com.pig4cloud.pigx.app.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
import com.pig4cloud.pigx.app.api.entity.AppArticleCollectEntity;
import com.pig4cloud.pigx.app.mapper.AppArticleCollectMapper;
import com.pig4cloud.pigx.app.service.AppArticleCollectService;
@@ -3,7 +3,7 @@ package com.pig4cloud.pigx.app.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.pig4cloud.pigx.app.api.entity.AppArticleCategoryEntity;
import com.pig4cloud.pigx.app.api.entity.AppArticleCollectEntity;
@@ -13,6 +13,13 @@ import com.pig4cloud.pigx.app.mapper.AppArticleMapper;
import com.pig4cloud.pigx.app.service.AppArticleService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Objects;
@@ -32,19 +39,17 @@ public class AppArticleServiceImpl extends ServiceImpl<AppArticleMapper, AppArti
private final AppArticleCollectMapper collectMapper;
/**
* 获取文章详情,并尝试将阅读数加 1。
* <p>
* 如果传入用户 ID,会额外查询该用户是否已收藏当前文章。
* 阅读数更新使用当前 visit 值作为条件,降低并发覆盖风险。
*
* @param id 文章 ID
* @param userId 当前用户 ID,可为空
* @return 文章详情
* 获取文章详情,并尝试将阅读数加 1。
* <p>
* 如果传入用户 ID,会额外查询该用户是否已收藏当前文章。 阅读数更新使用当前 visit 值作为条件,降低并发覆盖风险。
* @param id 文章 ID
* @param userId 当前用户 ID,可为空
* @return 文章详情
*/
@Override
public AppArticleEntity getArticleAndIncrById(Long id, Long userId) {
AppArticleEntity appArticleEntity = baseMapper.selectById(id);
// 登录用户访问详情时,补充当前用户是否已收藏该文章。
// 登录用户访问详情时,补充当前用户是否已收藏该文章。
if (Objects.nonNull(userId)) {
boolean exists = collectMapper.exists(Wrappers.<AppArticleCollectEntity>lambdaQuery()//
.eq(AppArticleCollectEntity::getArticleId, appArticleEntity.getId())//
@@ -54,7 +59,7 @@ public class AppArticleServiceImpl extends ServiceImpl<AppArticleMapper, AppArti
Integer nowVisit = appArticleEntity.getVisit();
appArticleEntity.setVisit(nowVisit + 1);
// 使用旧 visit 值作为更新条件,避免并发访问时直接覆盖最新阅读数。
// 使用旧 visit 值作为更新条件,避免并发访问时直接覆盖最新阅读数。
baseMapper.update(appArticleEntity,
Wrappers.<AppArticleEntity>lambdaQuery()
.eq(AppArticleEntity::getId, id)
@@ -63,11 +68,10 @@ public class AppArticleServiceImpl extends ServiceImpl<AppArticleMapper, AppArti
}
/**
* 分页查询文章列表,并关联分类名称。
*
* 分页查询文章列表,并关联分类名称。
* @param page 分页参数
* @param appArticle 文章查询条件
* @return 文章分页结果,包含分类名称 cname
* @return 文章分页结果,包含分类名称 cname
*/
@Override
public Page pageAndCname(Page page, AppArticleEntity appArticle) {
@@ -77,8 +81,42 @@ public class AppArticleServiceImpl extends ServiceImpl<AppArticleMapper, AppArti
.leftJoin(AppArticleCategoryEntity.class, AppArticleCategoryEntity::getId, AppArticleEntity::getCid)
.like(StrUtil.isNotBlank(appArticle.getAuthor()), AppArticleEntity::getAuthor, appArticle.getAuthor())
.like(StrUtil.isNotBlank(appArticle.getTitle()), AppArticleEntity::getTitle, appArticle.getTitle())
.eq(Objects.nonNull(appArticle.getCid()), AppArticleEntity::getCid, appArticle.getCid());
.eq(Objects.nonNull(appArticle.getCid()), AppArticleEntity::getCid, appArticle.getCid())
.orderByAsc(AppArticleEntity::getSort, AppArticleEntity::getId);
return this.page(page, wrapper);
}
/**
* 按传入 ID 顺序调整文章,保留未选中记录在排序序列中的位置。
* <p>
* 以当前租户内未删除记录的排序值、主键升序为基准,仅重排选中记录占据的位置, 再将全部记录的排序值从 1 起连续重编号。
* @param ids 按目标顺序排列的文章 ID;不可为 null 或空,不可包含 null、重复或不可用的 ID
* @throws IllegalArgumentException ID 列表为空、包含 null 或重复值,或指定记录已删除、不属于当前租户 或不存在
* @throws IllegalStateException 批量更新返回失败
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void sort(List<Long> ids) {
Assert.notEmpty(ids, "排序记录不能为空");
Set<Long> selected = new HashSet<>(ids);
Assert.isTrue(!selected.contains(null) && selected.size() == ids.size(), "排序记录无效");
// 锁定统一顺序,避免重复排序值和分页边界导致拖动后顺序丢失。
List<AppArticleEntity> records = list(Wrappers.<AppArticleEntity>lambdaQuery()
.orderByAsc(AppArticleEntity::getSort, AppArticleEntity::getId)
.last("FOR UPDATE"));
long matched = records.stream().filter(row -> selected.contains(row.getId())).count();
Assert.isTrue(matched == ids.size(), "记录已变更,请刷新后重试");
List<AppArticleEntity> updates = new ArrayList<>();
int next = 0;
for (int i = 0; i < records.size(); i++) {
AppArticleEntity current = records.get(i);
Long id = selected.contains(current.getId()) ? ids.get(next++) : current.getId();
AppArticleEntity update = new AppArticleEntity();
update.setId(id);
update.setSort(i + 1);
updates.add(update);
}
Assert.state(updateBatchById(updates), "排序保存失败");
}
}
@@ -4,7 +4,7 @@ import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
import com.pig4cloud.pigx.app.api.entity.AppContactsEntity;
import com.pig4cloud.pigx.app.mapper.AppContactsMapper;
import com.pig4cloud.pigx.app.service.AppContactsService;
@@ -59,7 +59,7 @@ public class AppMobileServiceImpl implements AppMobileService {
*/
@Override
public R<Boolean> sendSmsCode(String mobile) {
String codeObj = RedisUtils.get(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT + mobile);
String codeObj = RedisUtils.get(CacheConstants.SMS_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT + mobile);
if (StrUtil.isNotBlank(codeObj)) {
log.info("手机号验证码未过期:{},{}", mobile, codeObj);
@@ -68,7 +68,7 @@ public class AppMobileServiceImpl implements AppMobileService {
String code = RandomUtil.randomNumbers(Integer.parseInt(SecurityConstants.CODE_SIZE));
log.debug("手机号生成验证码成功:{},{}", mobile, code);
RedisUtils.set(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT + mobile, code, SecurityConstants.CODE_TIME);
RedisUtils.set(CacheConstants.SMS_CODE_KEY + LoginTypeEnum.APPSMS.getType() + StringPool.AT + mobile, code, SecurityConstants.CODE_TIME);
return R.ok(Boolean.TRUE);
}
@@ -1,6 +1,6 @@
package com.pig4cloud.pigx.app.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
import com.pig4cloud.pigx.app.api.entity.AppPageEntity;
import com.pig4cloud.pigx.app.mapper.AppPageMapper;
import com.pig4cloud.pigx.app.service.AppPageService;
@@ -2,7 +2,7 @@ package com.pig4cloud.pigx.app.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
import com.pig4cloud.pigx.app.api.dto.AppRoleTabbarDTO;
import com.pig4cloud.pigx.app.api.entity.AppRoleTabbar;
import com.pig4cloud.pigx.app.mapper.AppRoleTabbarMapper;
@@ -1,6 +1,6 @@
package com.pig4cloud.pigx.app.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.spring.service.impl.ServiceImpl;
import com.pig4cloud.pigx.app.api.entity.AppTabbarEntity;
import com.pig4cloud.pigx.app.mapper.AppTabbarMapper;
import com.pig4cloud.pigx.app.service.AppTabbarService;
+1 -1
View File
@@ -7,7 +7,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-app-server</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-auth</artifactId>
@@ -18,6 +18,8 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.awt.Color;
/**
* 验证码相关的接口
*
@@ -39,7 +41,7 @@ public class ImageCodeEndpoint {
@SneakyThrows
@GetMapping("/image")
public void image(String randomStr, HttpServletResponse response) {
ArithmeticCaptcha captcha = new ArithmeticCaptcha(DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT);
ArithmeticCaptcha captcha = createCaptcha();
String result = captcha.text();
@@ -48,11 +50,20 @@ public class ImageCodeEndpoint {
return;
}
RedisUtils.set(CacheConstants.DEFAULT_CODE_KEY + randomStr, result, SecurityConstants.CODE_TIME);
RedisUtils.set(CacheConstants.IMAGE_CODE_KEY + randomStr, result, SecurityConstants.CODE_TIME);
// 转换流信息写出
captcha.out(response.getOutputStream());
}
static ArithmeticCaptcha createCaptcha() {
return new ArithmeticCaptcha(DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT) {
@Override
protected Color color() {
return Color.BLACK;
}
};
}
/**
* 行为 创建滑块图形验证码
*/
@@ -6,6 +6,7 @@ import cn.hutool.json.JSONUtil;
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.constant.enums.CaptchaFlagTypeEnum;
import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum;
import com.pig4cloud.pigx.common.core.exception.ValidateCodeException;
import com.pig4cloud.pigx.common.core.util.MsgUtils;
import com.pig4cloud.pigx.common.data.cache.RedisUtils;
@@ -75,7 +76,7 @@ public class AuthCaptchaSupport {
*/
public boolean isCaptchaEnabled(String tenantId, String clientId) {
if (StrUtil.isBlank(clientId)) {
return false;
return true;
}
String finalTenantId = StrUtil.isBlank(tenantId) ? CommonConstants.TENANT_ID_1.toString() : tenantId;
@@ -103,12 +104,6 @@ public class AuthCaptchaSupport {
}
String randomStr = request.getParameter("randomStr");
// https://gitee.com/log4j/pig/issues/IWA0D 手机号场景以 mobile 作为缓存键
String mobile = request.getParameter("mobile");
if (StrUtil.isNotBlank(mobile)) {
randomStr = mobile;
}
boolean isBehavior = StrUtil.equalsAnyIgnoreCase(randomStr, CommonConstants.IMAGE_CODE_BLOCK_PUZZLE,
CommonConstants.IMAGE_CODE_CLICK_WORD);
String captchaType = isBehavior ? randomStr : CommonConstants.IMAGE_CODE_MATH;
@@ -122,6 +117,36 @@ public class AuthCaptchaSupport {
}
}
/**
* 按完整类型前缀判断是否使用短信登录,不校验手机号格式或验证码。
*
* @param mobile 登录标识,格式为 {@code SMS@手机号} 或 {@code APP-SMS@手机号};允许为 null
* @return 匹配短信类型前缀时为 true;null、空串或其他类型返回 false
*/
public boolean isSmsLogin(String mobile) {
return StrUtil.startWith(mobile, LoginTypeEnum.SMS.getType() + StrUtil.AT)
|| StrUtil.startWith(mobile, LoginTypeEnum.APPSMS.getType() + StrUtil.AT);
}
/**
* 校验请求中的短信身份凭据,不接受图形或行为验证码作为替代。
* 类型合法且验证码非空时委托缓存校验;读取缓存后无论匹配与否均消费该验证码。
*
* @param request 非 null 请求,mobile 参数为 {@code SMS@手机号} 或
* {@code APP-SMS@手机号},code 参数为短信验证码
* @throws ValidateCodeException 验证码为空、登录类型不合法,或验证码不存在、过期、不匹配
*/
public void validateSmsCode(HttpServletRequest request) {
String mobile = request.getParameter("mobile");
String code = request.getParameter("code");
if (StrUtil.isBlank(code)) {
throw new ValidateCodeException(MsgUtils.getMessage(AuthErrorCodes.AUTH_CAPTCHA_EMPTY));
}
if (!isSmsLogin(mobile) || !captchaValidator.validateSms(mobile, code).isOk()) {
throw new ValidateCodeException(MsgUtils.getMessage(AuthErrorCodes.AUTH_CAPTCHA_INVALID));
}
}
private String resolveAuthorizationParameter(HttpServletRequest request, HttpServletResponse response,
String parameterName, boolean includeCurrentRequestId) {
if (includeCurrentRequestId) {
@@ -1,8 +1,9 @@
package com.pig4cloud.pigx.auth.support.filter;
import cn.hutool.core.util.ArrayUtil;
import com.pig4cloud.pigx.auth.support.core.AuthCaptchaSupport;
import com.pig4cloud.pigx.auth.support.handler.FormAuthenticationFailureHandler;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.exception.ValidateCodeException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
@@ -11,6 +12,7 @@ import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -45,6 +47,14 @@ public class FormLoginValidateCodeFilter extends OncePerRequestFilter {
return;
}
// 表单登录只允许密码认证,防止切换到 APP 模式的免密码校验分支。
String[] grantTypes = request.getParameterValues(OAuth2ParameterNames.GRANT_TYPE);
if (ArrayUtil.length(grantTypes) != 1 || !ArrayUtil.contains(grantTypes, SecurityConstants.PASSWORD)) {
failureHandler.onAuthenticationFailure(request, response,
new AuthenticationServiceException("Form login only supports password grant"));
return;
}
String authClientId = authCaptchaSupport.resolveAuthorizationClientId(request, response, true);
String tenantId = authCaptchaSupport.resolveAuthorizationTenantId(request, response, true);
@@ -12,20 +12,22 @@ import com.pig4cloud.pigx.auth.support.core.AuthCaptchaSupport;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.exception.ValidateCodeException;
import com.pig4cloud.pigx.common.core.util.WebUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.Objects;
/**
* @author lbw
@@ -54,45 +56,30 @@ public class ValidateCodeFilter extends OncePerRequestFilter {
// 如果登录URL 但是刷新token的请求,直接向下执行
String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
if (StrUtil.containsAny(grantType, SecurityConstants.REFRESH_TOKEN)) {
if (StrUtil.equals(grantType, SecurityConstants.REFRESH_TOKEN)) {
filterChain.doFilter(request, response);
return;
}
// mobile模式, 如果请求不包含mobile 参数直接
String mobile = request.getParameter(SecurityConstants.GRANT_MOBILE);
if (StrUtil.equals(SecurityConstants.GRANT_MOBILE, grantType) && StrUtil.isBlank(mobile)) {
throw new OAuth2AuthenticationException(SecurityConstants.GRANT_MOBILE);
}
// mobile模式, 社交登录模式不校验验证码直接跳过
if (StrUtil.equals(SecurityConstants.GRANT_MOBILE, grantType) && !StrUtil.contains(mobile, "SMS")) {
filterChain.doFilter(request, response);
return;
}
// 判断客户端是否跳过检验
if (!isCheckCaptchaClient(request)) {
filterChain.doFilter(request, response);
return;
}
// 校验验证码 1. 客户端开启验证码 2. 短信模式
try {
checkCode();
if (StrUtil.equals(SecurityConstants.GRANT_MOBILE, grantType)) {
String mobile = request.getParameter(SecurityConstants.GRANT_MOBILE);
if (StrUtil.isBlank(mobile)) {
throw new OAuth2AuthenticationException(SecurityConstants.GRANT_MOBILE);
}
// 短信是身份凭据,不能随客户端图形验证码开关关闭。
if (authCaptchaSupport.isSmsLogin(mobile)) {
authCaptchaSupport.validateSmsCode(request);
}
} else if (isCheckCaptchaClient(request)) {
authCaptchaSupport.validateCode(request);
}
filterChain.doFilter(request, response);
} catch (ValidateCodeException validateCodeException) {
throw new OAuth2AuthenticationException(validateCodeException.getMessage());
}
}
/**
* 校验验证码
*/
private void checkCode() throws ValidateCodeException {
authCaptchaSupport.validateCode(WebUtils.getRequest());
}
/**
* 是否需要校验客户端,根据client 查询客户端配置
*
@@ -100,8 +87,12 @@ public class ValidateCodeFilter extends OncePerRequestFilter {
* @return true 需要校验, false 不需要校验
*/
private boolean isCheckCaptchaClient(HttpServletRequest request) {
String header = request.getHeader(HttpHeaders.AUTHORIZATION);
String clientId = WebUtils.extractClientId(header).orElse(null);
// 客户端认证过滤器在本过滤器之前执行,兼容 Basic 和 client_secret_post,
// 不信任额外传入的 client_id 来决定验证码策略。
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String clientId = authentication instanceof OAuth2ClientAuthenticationToken client
&& client.isAuthenticated() && Objects.nonNull(client.getRegisteredClient())
? client.getRegisteredClient().getClientId() : null;
// 获取租户拼接区分租户的key
String tenantId = request.getHeader(CommonConstants.TENANT_ID);
return authCaptchaSupport.isCaptchaEnabled(tenantId, clientId);
@@ -0,0 +1,75 @@
package com.pig4cloud.pigx.auth.support.filter;
import com.pig4cloud.pigx.auth.support.core.AuthCaptchaSupport;
import jakarta.servlet.FilterChain;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
class FormLoginValidateCodeFilterTests {
private final AuthCaptchaSupport captchaSupport = mock(AuthCaptchaSupport.class);
private final FormLoginValidateCodeFilter filter = new FormLoginValidateCodeFilter(captchaSupport);
private final FilterChain chain = mock(FilterChain.class);
private final MockHttpServletRequest request = new MockHttpServletRequest("POST", "/oauth2/form");
private final MockHttpServletResponse response = new MockHttpServletResponse();
@AfterEach
void cleanup() {
RequestContextHolder.resetRequestAttributes();
}
static Stream<Arguments> invalidGrants() {
return Stream.of(null, new String[] { "" }, new String[] { "app" }, new String[] { "mobile" },
new String[] { "client_credentials" }, new String[] { "authorization_code" },
new String[] { "refresh_token" }, new String[] { "password", "app" },
new String[] { "app", "password" }, new String[] { "password", "password" }).map(values -> Arguments.of((Object) values));
}
@ParameterizedTest
@MethodSource("invalidGrants")
void rejectsInvalidGrantsBeforeCaptchaOrAuthentication(String[] grantTypes) throws Exception {
if (grantTypes != null) {
request.setParameter("grant_type", grantTypes);
}
applyFilter();
assertThat(response.getStatus()).isEqualTo(302);
assertThat(response.getRedirectedUrl()).contains("/token/login?error=");
verifyNoInteractions(captchaSupport, chain);
}
@ParameterizedTest
@ValueSource(booleans = { false, true })
void allowsPasswordWithExistingTenantCaptchaPolicy(boolean captchaEnabled) throws Exception {
request.setParameter("grant_type", "password");
when(captchaSupport.resolveAuthorizationClientId(request, response, true)).thenReturn("client");
when(captchaSupport.resolveAuthorizationTenantId(request, response, true)).thenReturn("2");
when(captchaSupport.isCaptchaEnabled("2", "client")).thenReturn(captchaEnabled);
applyFilter();
verify(captchaSupport).isCaptchaEnabled("2", "client");
verify(captchaSupport, times(captchaEnabled ? 1 : 0)).validateCode(request);
verify(chain).doFilter(request, response);
}
private void applyFilter() throws Exception {
request.setServletPath("/oauth2/form");
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response));
filter.doFilter(request, response, chain);
}
}
@@ -0,0 +1,139 @@
package com.pig4cloud.pigx.auth.support.filter;
import com.pig4cloud.pigx.auth.endpoint.ImageCodeEndpoint;
import com.pig4cloud.pigx.auth.support.core.AuthCaptchaSupport;
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
import com.pig4cloud.pigx.common.security.captcha.CaptchaValidator;
import jakarta.servlet.FilterChain;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.StaticMessageSource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
class LoginCaptchaSecurityTests {
private final Map<String, Object> cache = new HashMap<>();
private final RedisTemplate<String, Object> redis = mock(RedisTemplate.class);
private final ValueOperations<String, Object> values = mock(ValueOperations.class);
private final ValidateCodeFilter filter = new ValidateCodeFilter(
new AuthCaptchaSupport(new CaptchaValidator(null, redis)));
private final MockHttpServletRequest request = new MockHttpServletRequest("POST", "/oauth2/token");
private final MockHttpServletResponse response = new MockHttpServletResponse();
private final FilterChain chain = mock(FilterChain.class);
@BeforeEach
void setup() {
when(redis.opsForValue()).thenReturn(values);
when(values.get(anyString())).thenAnswer(invocation -> cache.get(invocation.getArgument(0)));
when(values.getAndDelete(anyString())).thenAnswer(invocation -> cache.remove(invocation.getArgument(0)));
doAnswer(invocation -> {
cache.put(invocation.getArgument(0), invocation.getArgument(1));
return null;
}).when(values).set(anyString(), any(), anyLong(), any(TimeUnit.class));
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerSingleton("redisTemplate", redis);
StaticMessageSource messages = new StaticMessageSource();
messages.setUseCodeAsDefaultMessage(true);
factory.registerSingleton("pigxMessageSource", messages);
new SpringContextHolder().postProcessBeanFactory(factory);
request.setServletPath("/oauth2/token");
request.addHeader("TENANT-ID", "1");
}
@AfterEach
void cleanup() {
SecurityContextHolder.clearContext();
new SpringContextHolder().postProcessBeanFactory(null);
SpringContextHolder.clearHolder();
}
static Stream<Arguments> validCases() {
return Stream.of(Arguments.of("SMS", false, false), Arguments.of("SMS", true, false),
Arguments.of("SMS", false, true), Arguments.of("SMS", true, true),
Arguments.of("APP-SMS", true, true), Arguments.of("image", false, true),
Arguments.of("image", true, true));
}
@ParameterizedTest
@MethodSource("validCases")
void acceptsOnlyTheExpectedCaptchaAndConsumesIt(String type, boolean post, boolean enabled) throws Exception {
configureClient(post, enabled);
String key;
if (type.equals("image")) {
request.setParameter("grant_type", "password");
request.setParameter("randomStr", "image-test");
new ImageCodeEndpoint().image("image-test", response);
key = CacheConstants.IMAGE_CODE_KEY + "image-test";
} else {
request.setParameter("grant_type", "mobile");
request.setParameter("mobile", type + "@13800138000");
key = CacheConstants.SMS_CODE_KEY + type + "@13800138000";
cache.put(key, "654321");
}
request.setParameter("code", cache.get(key).toString());
filter.doFilter(request, response, chain);
verify(chain).doFilter(request, response);
assertThat(cache).doesNotContainKey(key);
}
static Stream<Arguments> invalidCases() {
Stream<Arguments> smsCases = Stream.of(false, true).flatMap(post -> Stream.of(false, true).flatMap(enabled ->
Stream.of("missing", "image", "legacy").map(kind -> Arguments.of(post, enabled, kind))));
return Stream.concat(smsCases, Stream.of(Arguments.of(false, true, "password"),
Arguments.of(true, true, "password")));
}
@ParameterizedTest
@MethodSource("invalidCases")
void rejectsMissingOrSubstitutedIdentityProof(boolean post, boolean enabled, String kind) throws Exception {
configureClient(post, enabled);
String mobile = "SMS@13800138000";
request.setParameter("grant_type", kind.equals("password") ? "password" : "mobile");
request.setParameter("mobile", mobile);
if (kind.equals("image")) {
new ImageCodeEndpoint().image(mobile, response);
assertThat(cache).doesNotContainKey(CacheConstants.SMS_CODE_KEY + mobile);
request.setParameter("code", cache.get(CacheConstants.IMAGE_CODE_KEY + mobile).toString());
} else if (kind.equals("legacy")) {
cache.put(CacheConstants.DEFAULT_CODE_KEY + mobile, "123456");
request.setParameter("code", "123456");
}
assertThatThrownBy(() -> filter.doFilter(request, response, chain))
.isInstanceOf(OAuth2AuthenticationException.class);
verifyNoInteractions(chain);
}
private void configureClient(boolean post, boolean enabled) {
RegisteredClient client = RegisteredClient.withId("test-client").clientId("test-client")
.authorizationGrantType(new AuthorizationGrantType("password")).build();
SecurityContextHolder.getContext().setAuthentication(new OAuth2ClientAuthenticationToken(client,
post ? ClientAuthenticationMethod.CLIENT_SECRET_POST : ClientAuthenticationMethod.CLIENT_SECRET_BASIC,
"test-secret"));
// 策略只取已认证客户端,不使用请求额外指定的客户端。
request.setParameter("client_id", "untrusted-client");
cache.put("1:client_config_flag:test-client", "{\"captcha_flag\":\"" + (enabled ? "1" : "0") + "\"}");
cache.put("1:client_config_flag:untrusted-client", "{\"captcha_flag\":\"0\"}");
}
}
+1 -1
View File
@@ -21,7 +21,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-boot</artifactId>
@@ -26,6 +26,7 @@ spring:
redis.enabled: false
sentinel.enabled: false
openfeign.okhttp.enabled: true
openfeign.compression.request.min-request-size: 5120
freemarker: # freemarker 配置,授权码模式页面渲染使用
suffix: .ftl
template-loader-path: classpath:/templates/
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-audit</artifactId>
+9 -9
View File
@@ -7,18 +7,18 @@
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common-bom</artifactId>
<packaging>pom</packaging>
<version>${pigx.version}</version>
<version>6.2.0</version>
<description>pigx 公共版本控制,在此pom定义的依赖,全局业务微服务不需要指定版本。</description>
<properties>
<pigx.version>6.1.0</pigx.version>
<mybatis-plus.version>3.5.16</mybatis-plus.version>
<pigx.version>6.2.0</pigx.version>
<mybatis-plus.version>3.5.18.1</mybatis-plus.version>
<mybatis-spring-boot.version>4.0.1</mybatis-spring-boot.version>
<mybatis-plus-join.version>1.5.7</mybatis-plus-join.version>
<dynamic-ds.version>4.5.0</dynamic-ds.version>
<nacos.client.version>3.2.3</nacos.client.version>
<mybatis-plus-join.version>1.5.9</mybatis-plus-join.version>
<dynamic-ds.version>4.6.0</dynamic-ds.version>
<nacos.client.version>3.3.0</nacos.client.version>
<druid.version>1.2.28</druid.version>
<hutool.version>5.8.47</hutool.version>
<hutool.version>5.8.48</hutool.version>
<mysql.connector.version>9.5.0</mysql.connector.version>
<oracle.version>21.3.0.0</oracle.version>
<sqlserver.version>12.10.2.jre8</sqlserver.version>
@@ -30,13 +30,13 @@
<mp.weixin.version>4.7.9-20260103.233048</mp.weixin.version>
<ijpay.version>2.9.12.1-17</ijpay.version>
<groovy.version>3.0.25</groovy.version>
<jsoup.version>1.15.4</jsoup.version>
<jsoup.version>1.23.2</jsoup.version>
<aviator.version>5.4.4</aviator.version>
<flowable.version>8.0.0</flowable.version>
<fastjson.version>1.2.84</fastjson.version>
<xxl.job.version>3.4.2</xxl.job.version>
<aliyun.version>3.0.52.ALL</aliyun.version>
<aws.sdk.version>2.44.14</aws.sdk.version>
<aws.sdk.version>2.55.3</aws.sdk.version>
<javers.version>7.8.6</javers.version>
<seata.version>2.5.0</seata.version>
<asm.version>7.1</asm.version>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-core</artifactId>
@@ -20,6 +20,12 @@ public interface CacheConstants {
*/
String DEFAULT_CODE_KEY = "DEFAULT_CODE_KEY:";
/** 图形验证码独立命名空间,不能写入短信验证码缓存。 */
String IMAGE_CODE_KEY = "IMAGE_CODE_KEY:";
/** 短信验证码独立命名空间,后接登录类型与手机号。 */
String SMS_CODE_KEY = "SMS_CODE_KEY:";
/**
* 菜单信息缓存
*/
@@ -137,32 +137,11 @@ public interface SecurityConstants {
*/
String MINI_APP_PHONE_NUMBER_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s";
/**
* 码云获取token
*/
String GITEE_AUTHORIZATION_CODE_URL = "https://gitee.com/oauth/token?grant_type="
+ "authorization_code&code=%S&client_id=%s&redirect_uri=" + "%s&client_secret=%s";
/**
* 开源中国获取token
*/
String OSC_AUTHORIZATION_CODE_URL = "https://www.oschina.net/action/openapi/token";
/**
* QQ获取用户信息
*/
String QQ_USER_INFO_URL = "https://graph.qq.com/oauth2.0/me?access_token=%s";
/**
* 码云获取用户信息
*/
String GITEE_USER_INFO_URL = "https://gitee.com/api/v5/user?access_token=%s";
/**
* 开源中国用户信息
*/
String OSC_USER_INFO_URL = "https://www.oschina.net/action/openapi/user?access_token=%s&dataType=json";
/**
* 钉钉获取 token
*/
@@ -55,21 +55,14 @@ public enum LoginTypeEnum {
*/
MINI_APP("MINI", "微信小程序"),
/**
* 码云登录
*/
GITEE("GITEE", "码云登录"),
/**
* 开源中国登录
*/
OSC("OSC", "开源中国登录"),
/**
* 钉钉
*/
DINGTALK("DINGTALK", "钉钉"),
/** 企业飞书 */
FEISHU("FEISHU", "企业飞书"),
/**
* 企业微信
*/
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-data</artifactId>
@@ -1,9 +1,9 @@
package com.pig4cloud.pigx.common.data.datascope;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.github.yulichang.injector.MPJSqlInjector;
import org.apache.ibatis.session.Configuration;
import java.util.List;
@@ -16,8 +16,8 @@ import java.util.List;
public class DataScopeSqlInjector extends MPJSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
public List<AbstractMethod> getMethodList(Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {
List<AbstractMethod> methodList = super.getMethodList(configuration, mapperClass, tableInfo);
methodList.add(new SelectListByScope());
methodList.add(new SelectPageByScope());
methodList.add(new SelectCountByScope());
@@ -22,7 +22,7 @@ public class SelectCountByScope extends AbstractMethod {
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
SqlMethod sqlMethod = SqlMethod.SELECT_LIST;
String sql = String.format(sqlMethod.getSql(), this.sqlFirst(), this.sqlSelectColumns(tableInfo, true),
String sql = sqlMethod.format(this.sqlFirst(), this.sqlSelectColumns(tableInfo, true),
tableInfo.getTableName(), this.sqlWhereEntityWrapper(true, tableInfo), this.sqlOrderBy(tableInfo),
this.sqlComment());
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
@@ -19,7 +19,7 @@ public class SelectListByScope extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
SqlMethod sqlMethod = SqlMethod.SELECT_LIST;
String sql = String.format(sqlMethod.getSql(), this.sqlFirst(), this.sqlSelectColumns(tableInfo, true),
String sql = sqlMethod.format(this.sqlFirst(), this.sqlSelectColumns(tableInfo, true),
tableInfo.getTableName(), this.sqlWhereEntityWrapper(true, tableInfo), this.sqlOrderBy(tableInfo),
this.sqlComment());
SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, modelClass);
@@ -19,7 +19,7 @@ public class SelectPageByScope extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
SqlMethod sqlMethod = SqlMethod.SELECT_PAGE;
String sql = String.format(sqlMethod.getSql(), this.sqlFirst(), this.sqlSelectColumns(tableInfo, true),
String sql = sqlMethod.format(this.sqlFirst(), this.sqlSelectColumns(tableInfo, true),
tableInfo.getTableName(), this.sqlWhereEntityWrapper(true, tableInfo), this.sqlOrderBy(tableInfo),
this.sqlComment());
SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, modelClass);
@@ -20,9 +20,9 @@ package com.pig4cloud.pigx.common.data.resolver;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pigx.common.core.exception.CheckedException;
import com.pig4cloud.pigx.common.data.util.SqlSortUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
@@ -92,14 +92,16 @@ public class SqlFilterArgumentResolver implements HandlerMethodArgumentResolver
List<OrderItem> orderItemList = new ArrayList<>();
Optional.ofNullable(ascs)
.ifPresent(s -> orderItemList.addAll(Arrays.stream(s.split(StrUtil.COMMA))
.filter(asc -> !SqlInjectionUtils.check(asc))
.map(String::trim)
.filter(SqlSortUtils::isValidColumn)
.map(OrderItem::asc)
.toList()));
.toList()));
Optional.ofNullable(descs)
.ifPresent(s -> orderItemList.addAll(Arrays.stream(s.split(StrUtil.COMMA))
.filter(desc -> !SqlInjectionUtils.check(desc))
.map(String::trim)
.filter(SqlSortUtils::isValidColumn)
.map(OrderItem::desc)
.toList()));
.toList()));
page.addOrder(orderItemList);
return page;
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the pig4cloud.com developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: lengleng (wangiegie@gmail.com)
*/
package com.pig4cloud.pigx.common.data.util;
import java.util.regex.Pattern;
/**
* 前端排序字段校验。仅接受字段名或表别名限定的字段名,不接受 SQL 表达式。
* <p>
* 本工具只校验字段格式,业务层仍需限制允许排序的字段。
*/
public final class SqlSortUtils {
private static final Pattern SORT_COLUMN = Pattern.compile("[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)?");
private SqlSortUtils() {
}
/**
* 校验单个排序字段。调用方应先拆分多个字段并去除首尾空白。
* @param column 前端传入的排序字段
* @return 是否为合法的字段名或别名限定字段名
*/
public static boolean isValidColumn(String column) {
return column != null && SORT_COLUMN.matcher(column).matches();
}
}
@@ -0,0 +1,41 @@
package com.pig4cloud.pigx.common.data.datascope;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class DataScopeSqlInjectorTests {
@Test
void registersDataScopeAndJoinStatementsTogether() {
MybatisConfiguration configuration = new MybatisConfiguration();
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setDbConfig(new GlobalConfig.DbConfig());
globalConfig.setSqlInjector(new DataScopeSqlInjector());
GlobalConfigUtils.setGlobalConfig(configuration, globalConfig);
configuration.addMapper(TestMapper.class);
String namespace = TestMapper.class.getName() + ".";
assertThat(configuration.getMappedStatementNames()).contains(namespace + "selectListByScope",
namespace + "selectPageByScope", namespace + "selectCountByScope", namespace + "selectJoinList",
namespace + "selectJoinPage", namespace + "selectJoinCount", namespace + "selectById");
}
interface TestMapper extends PigxBaseMapper<TestEntity> {
}
@TableName("injector_test")
static class TestEntity {
@TableId
private Long id;
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>pigx-common</artifactId>
<groupId>com.pig4cloud</groupId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-encrypt-api</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-excel</artifactId>
@@ -13,32 +13,31 @@ import java.util.List;
*/
public final class BuiltinConverters {
private BuiltinConverters() {
}
private BuiltinConverters() {
}
/**
* 读写 Excel 时默认启用的转换器列表。
*/
private static final List<Converter<?>> INSTANCES = List.of(LocalDateStringConverter.INSTANCE,
LocalTimeStringConverter.INSTANCE, LocalDateTimeStringConverter.INSTANCE, LongStringConverter.INSTANCE,
StringArrayConverter.INSTANCE);
/**
* 读写 Excel 时默认启用的转换器列表。
*/
private static final List<Converter<?>> INSTANCES = List.of(LocalDateStringConverter.INSTANCE,
LocalTimeStringConverter.INSTANCE, LocalDateTimeStringConverter.INSTANCE, LongStringConverter.INSTANCE,
StringArrayConverter.INSTANCE);
/**
* 注册内置转换器到 Excel 写构造器。
*
* @param builder Excel 写构造器
*/
public static void registerTo(ExcelWriterBuilder builder) {
INSTANCES.forEach(builder::registerConverter);
}
/**
* 注册内置转换器到 Excel 写构造器。
* @param builder Excel 写构造器
*/
public static void registerTo(ExcelWriterBuilder builder) {
INSTANCES.forEach(builder::registerConverter);
builder.registerConverter(StringArrayConverter.WRITE_INSTANCE);
}
/**
* 注册内置转换器到 Excel 读构造器。
*
* @param builder Excel 读构造器
*/
public static void registerTo(ExcelReaderBuilder builder) {
INSTANCES.forEach(builder::registerConverter);
}
/**
* 注册内置转换器到 Excel 读构造器。
* @param builder Excel 读构造器
*/
public static void registerTo(ExcelReaderBuilder builder) {
INSTANCES.forEach(builder::registerConverter);
}
}
@@ -18,10 +18,24 @@ public enum StringArrayConverter implements Converter<String[]> {
/**
* 实例
*/
INSTANCE;
INSTANCE,
/**
* 未指定目标单元格类型时,FESOD 使用 null 作为导出转换器的类型键。
*/
WRITE_INSTANCE {
/**
* 提供未指定目标单元格类型时使用的导出转换器查找键。
* @return null,与 FESOD 默认导出时的目标单元格类型匹配
*/
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return null;
}
};
@Override
public Class<?> supportJavaTypeKey() {
public Class<?> supportJavaTypeKey() {
return String[].class;
}
@@ -31,15 +45,15 @@ public enum StringArrayConverter implements Converter<String[]> {
}
@Override
public String[] convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return StrUtil.splitToArray(cellData.getStringValue(), StrUtil.COMMA);
public String[] convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return StrUtil.splitToArray(cellData.getStringValue(), StrUtil.COMMA);
}
@Override
public WriteCellData<String> convertToExcelData(String[] value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return new WriteCellData<>(String.join(StrUtil.COMMA, value));
return new WriteCellData<>(String.join(StrUtil.COMMA, value));
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-gateway</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-gray</artifactId>
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-idempotent</artifactId>
@@ -5,8 +5,10 @@ import com.pig4cloud.pigx.common.idempotent.expression.ExpressionResolver;
import com.pig4cloud.pigx.common.idempotent.expression.KeyResolver;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.BaseConfig;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.SingleServerConfig;
import org.redisson.config.SentinelServersConfig;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -27,52 +29,74 @@ import org.springframework.util.StringUtils;
@EnableConfigurationProperties(DataRedisProperties.class)
public class IdempotentAutoConfiguration {
/**
* KEY解析器
* @return {@link KeyResolver }
*/
@Bean
@ConditionalOnMissingBean(KeyResolver.class)
public KeyResolver keyResolver() {
return new ExpressionResolver();
}
/**
* KEY解析器
*
* @return {@link KeyResolver }
*/
@Bean
@ConditionalOnMissingBean(KeyResolver.class)
public KeyResolver keyResolver() {
return new ExpressionResolver();
}
/**
* 幂等 Redis 客户端
* @param redisProperties Redis 连接配置
* @return Redisson 客户端
*/
@Bean
@ConditionalOnMissingBean(RedissonClient.class)
public RedissonClient redissonClient(DataRedisProperties redisProperties) {
Config config = new Config();
BaseConfig<?> serverConfig;
if (redisProperties.getSentinel() != null) {
DataRedisProperties.Sentinel sentinel = redisProperties.getSentinel();
SentinelServersConfig sentinelConfig = config.useSentinelServers()
.setMasterName(sentinel.getMaster())
.setDatabase(redisProperties.getDatabase());
for (String node : sentinel.getNodes()) {
sentinelConfig.addSentinelAddress("redis://" + node);
}
if (StringUtils.hasText(sentinel.getUsername())) {
sentinelConfig.setSentinelUsername(sentinel.getUsername());
}
if (StringUtils.hasText(sentinel.getPassword())) {
sentinelConfig.setSentinelPassword(sentinel.getPassword());
}
serverConfig = sentinelConfig;
}
else if (redisProperties.getCluster() != null) {
ClusterServersConfig clusterConfig = config.useClusterServers();
for (String node : redisProperties.getCluster().getNodes()) {
clusterConfig.addNodeAddress("redis://" + node);
}
serverConfig = clusterConfig;
}
else {
serverConfig = config.useSingleServer()
.setAddress("redis://" + redisProperties.getHost() + ":" + redisProperties.getPort())
.setDatabase(redisProperties.getDatabase());
}
/**
* 幂等方面
*
* @param redisson redisson 客户端
* @param keyResolver KEY解析器
* @return {@link IdempotentAspect }
*/
@Bean
@ConditionalOnMissingBean(RedissonClient.class)
public RedissonClient redissonClient(DataRedisProperties redisProperties) {
Config config = new Config();
SingleServerConfig singleServerConfig = config.useSingleServer()
.setAddress("redis://" + redisProperties.getHost() + ":" + redisProperties.getPort())
.setDatabase(redisProperties.getDatabase());
if (StringUtils.hasText(redisProperties.getUsername())) {
serverConfig.setUsername(redisProperties.getUsername());
}
if (StringUtils.hasText(redisProperties.getPassword())) {
serverConfig.setPassword(redisProperties.getPassword());
}
if (redisProperties.getConnectTimeout() != null) {
serverConfig.setConnectTimeout((int) redisProperties.getConnectTimeout().toMillis());
}
if (redisProperties.getTimeout() != null) {
serverConfig.setTimeout((int) redisProperties.getTimeout().toMillis());
}
if (StringUtils.hasText(redisProperties.getUsername())) {
singleServerConfig.setUsername(redisProperties.getUsername());
}
if (StringUtils.hasText(redisProperties.getPassword())) {
singleServerConfig.setPassword(redisProperties.getPassword());
}
if (redisProperties.getConnectTimeout() != null) {
singleServerConfig.setConnectTimeout((int) redisProperties.getConnectTimeout().toMillis());
}
if (redisProperties.getTimeout() != null) {
singleServerConfig.setTimeout((int) redisProperties.getTimeout().toMillis());
}
return Redisson.create(config);
}
return Redisson.create(config);
}
@Bean
public IdempotentAspect idempotentAspect(RedissonClient redisson, KeyResolver keyResolver) {
return new IdempotentAspect(redisson, keyResolver);
}
@Bean
public IdempotentAspect idempotentAspect(RedissonClient redisson, KeyResolver keyResolver) {
return new IdempotentAspect(redisson, keyResolver);
}
}
@@ -0,0 +1,91 @@
package com.pig4cloud.pigx.common.idempotent;
import java.time.Duration;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.redisson.config.SentinelServersConfig;
import org.springframework.boot.data.redis.autoconfigure.DataRedisProperties;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.withSettings;
class IdempotentAutoConfigurationTests {
@Test
void usesClusterNodesInsteadOfDefaultSingleServer() {
DataRedisProperties properties = new DataRedisProperties();
properties.setUsername("cluster-user");
properties.setPassword("cluster-password");
properties.setConnectTimeout(Duration.ofSeconds(2));
properties.setTimeout(Duration.ofSeconds(3));
DataRedisProperties.Cluster cluster = new DataRedisProperties.Cluster();
cluster.setNodes(List.of("redis-1:7000", "redis-2:7001", "redis-3:7002"));
properties.setCluster(cluster);
RedissonClient client = mock(RedissonClient.class);
try (MockedStatic<Redisson> redisson = mockStatic(Redisson.class,
withSettings().mockMaker("mock-maker-inline"))) {
redisson.when(() -> Redisson.create(any(Config.class))).thenAnswer(invocation -> {
Config config = invocation.getArgument(0);
assertThat(config.isClusterConfig()).as("必须使用集群模式连接").isTrue();
ClusterServersConfig servers = config.useClusterServers();
assertThat(servers.getNodeAddresses()).containsExactly("redis://redis-1:7000", "redis://redis-2:7001",
"redis://redis-3:7002");
assertThat(servers.getUsername()).isEqualTo("cluster-user");
assertThat(servers.getPassword()).isEqualTo("cluster-password");
assertThat(servers.getConnectTimeout()).isEqualTo(2000);
assertThat(servers.getTimeout()).isEqualTo(3000);
return client;
});
assertThat(new IdempotentAutoConfiguration().redissonClient(properties)).isSameAs(client);
}
}
@Test
void usesSentinelConfigurationInsteadOfDefaultSingleServer() {
DataRedisProperties properties = new DataRedisProperties();
properties.setDatabase(2);
properties.setUsername("data-user");
properties.setPassword("data-password");
properties.setConnectTimeout(Duration.ofSeconds(2));
properties.setTimeout(Duration.ofSeconds(3));
DataRedisProperties.Sentinel sentinel = new DataRedisProperties.Sentinel();
sentinel.setMaster("mymaster");
sentinel.setNodes(List.of("sentinel-1:26379", "sentinel-2:26379"));
sentinel.setUsername("sentinel-user");
sentinel.setPassword("sentinel-password");
properties.setSentinel(sentinel);
RedissonClient client = mock(RedissonClient.class);
try (MockedStatic<Redisson> redisson = mockStatic(Redisson.class,
withSettings().mockMaker("mock-maker-inline"))) {
redisson.when(() -> Redisson.create(any(Config.class))).thenAnswer(invocation -> {
Config config = invocation.getArgument(0);
assertThat(config.isSentinelConfig()).as("必须使用哨兵模式连接").isTrue();
SentinelServersConfig servers = config.useSentinelServers();
assertThat(servers.getMasterName()).isEqualTo("mymaster");
assertThat(servers.getSentinelAddresses()).containsExactly("redis://sentinel-1:26379",
"redis://sentinel-2:26379");
assertThat(servers.getSentinelUsername()).isEqualTo("sentinel-user");
assertThat(servers.getSentinelPassword()).isEqualTo("sentinel-password");
assertThat(servers.getUsername()).isEqualTo("data-user");
assertThat(servers.getPassword()).isEqualTo("data-password");
assertThat(servers.getDatabase()).isEqualTo(2);
assertThat(servers.getConnectTimeout()).isEqualTo(2000);
assertThat(servers.getTimeout()).isEqualTo(3000);
return client;
});
assertThat(new IdempotentAutoConfiguration().redissonClient(properties)).isSameAs(client);
}
}
}
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-job</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-log</artifactId>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-oss</artifactId>
+1 -1
View File
@@ -23,7 +23,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-seata</artifactId>
+1 -1
View File
@@ -23,7 +23,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-security</artifactId>
@@ -68,14 +68,31 @@ public class CaptchaValidator {
return CaptchaResult.failed("验证码参数不能为空");
}
String key = CacheConstants.DEFAULT_CODE_KEY + randomStr;
Object codeObj = redisTemplate.opsForValue().get(key);
return validateCachedCode(CacheConstants.IMAGE_CODE_KEY + randomStr, code);
}
/**
* 从短信专用缓存校验身份凭据,不读取图形或行为验证码缓存。
* 非空参数触发原子读取并删除,无论匹配与否均消费该缓存中的验证码。
*
* @param mobile 短信登录标识,格式为类型@手机号;调用方负责校验允许的登录类型
* @param code 待校验的短信验证码
* @return 匹配时返回成功;任一参数为 null、空白,或验证码不存在、过期、不匹配时返回失败;
* 参数为空时不访问缓存
*/
public CaptchaResult validateSms(String mobile, String code) {
if (StrUtil.hasBlank(mobile, code)) {
return CaptchaResult.failed("验证码参数不能为空");
}
return validateCachedCode(CacheConstants.SMS_CODE_KEY + mobile, code);
}
private CaptchaResult validateCachedCode(String key, String code) {
Object codeObj = redisTemplate.opsForValue().getAndDelete(key);
if (codeObj == null || StrUtil.isBlank(codeObj.toString()) || !StrUtil.equals(codeObj.toString(), code)) {
redisTemplate.delete(key);
return CaptchaResult.failed("验证码错误或已过期");
}
redisTemplate.delete(key);
return CaptchaResult.ok();
}
@@ -2,13 +2,13 @@ package com.pig4cloud.pigx.common.security.component;
import cn.hutool.core.net.Ipv4Util;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.net.IPv6Utils;
import org.jetbrains.annotations.NotNull;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpStatus;
@@ -39,23 +39,23 @@ public class PigxInnerHeaderRemoveFilter extends OncePerRequestFilter {
*/
@Override
protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
// 获取客户端IP地址
String clientIP = JakartaServletUtil.getClientIP(request);
// 使用容器提供的连接地址,不从调用者可填写的转发头提取 IP。
String clientIP = request.getRemoteAddr();
// 允许 127.0.0.1 请求直接通过
if (Ipv4Util.LOCAL_IP.equals(clientIP)) {
// 兼容 IPv4 回环不解析主机名。
if (StrUtil.equalsAny(clientIP, Ipv4Util.LOCAL_IP)) {
filterChain.doFilter(request, response);
return;
}
String fromHeader = request.getHeader(SecurityConstants.FROM);
// 非 127.0.0.1 请求,没有配置 from header ,直接跳过
// 普通请求不携带内部标记,交给后续认证与权限检查。
if (StrUtil.isBlank(fromHeader)) {
filterChain.doFilter(request, response);
return;
}
// 非 127.0.0.1,配置了 from header ,直接拒绝访问
// 外部连接或经过代理的请求,不允许自行声明为内部调用。
log.warn("请求 IP {},疑似进行系统注入操作,请注意", clientIP);
response.setStatus(HttpStatus.FORBIDDEN.value());
}
@@ -56,7 +56,7 @@ public class PigxResourceServerConfiguration {
private final PigxBootCorsProperties corsProperties;
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
PathPatternRequestMatcher[] permitMatchers = permitAllUrl.getIgnoreUrls()
.stream()
@@ -22,7 +22,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
@@ -40,7 +39,8 @@ import java.util.Map;
public class PigxUser extends User implements OAuth2AuthenticatedPrincipal {
@Serial
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
// 保留原序列化版本,兼容已有授权缓存。
private static final long serialVersionUID = 620L;
/**
* 扩展属性,方便存放oauth 上下文相关信息
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-sensitive</artifactId>
+11 -1
View File
@@ -5,7 +5,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -55,5 +55,15 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-jackson2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
@@ -108,6 +109,19 @@ public class GlobalBizExceptionHandler {
return R.failed("数据库调用异常,请联系管理员处理");
}
/**
* Spring 包装的数据访问异常,避免向客户端回显 SQL 和数据库结构
* @param exception 数据访问异常
* @return 统一失败响应,不向客户端暴露底层 SQL 细节
*/
@ExceptionHandler(DataAccessException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R handleDataAccessException(DataAccessException exception) {
log.error("数据库调用异常 ex={}", exception.getMessage(), exception);
Tracer.trace(exception);
return R.failed("数据库调用异常,请联系管理员处理");
}
/**
* Feign 远程调用异常,透传远端 R 响应体
* @param e Feign 调用异常
@@ -0,0 +1,39 @@
package com.pig4cloud.pigx.common.sentinel.handle;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.util.R;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver;
import java.lang.reflect.Method;
import java.sql.SQLException;
import static org.assertj.core.api.Assertions.assertThat;
class GlobalBizExceptionHandlerTests {
@Test
void dataAccessExceptionMustNotExposeSql() throws Exception {
String sql = "select password, salt from sys_user where username = 'admin'";
BadSqlGrammarException exception = new BadSqlGrammarException("query", sql, new SQLException("syntax error"));
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(GlobalBizExceptionHandler.class);
Method method = resolver.resolveMethodByThrowable(exception);
assertThat(method).isNotNull();
try (Entry entry = SphU.entry("dataAccessExceptionMustNotExposeSql")) {
R<?> result = (R<?>) method.invoke(new GlobalBizExceptionHandler(), exception);
assertThat(result.getCode()).isEqualTo(CommonConstants.FAIL);
assertThat(result.getMsg()).isEqualTo("数据库调用异常,请联系管理员处理");
assertThat(result.getData()).isNull();
assertThat(method.getAnnotation(ResponseStatus.class).value()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
assertThat(entry.getError()).isSameAs(exception);
}
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-sequence</artifactId>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-sse</artifactId>
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-swagger</artifactId>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-websocket</artifactId>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-common</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common-xss</artifactId>
@@ -100,7 +100,7 @@ public class XssUtil {
}
@Override
protected boolean isSafeAttribute(String tagName, Element el, Attribute attr) {
public boolean isSafeAttribute(String tagName, Element el, Attribute attr) {
// 不允许 javascript 开头的 src 和 href
if ("src".equalsIgnoreCase(attr.getKey()) || "href".equalsIgnoreCase(attr.getKey())) {
String value = attr.getValue();
+1 -1
View File
@@ -24,7 +24,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-common</artifactId>
+1 -1
View File
@@ -7,7 +7,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-flow</artifactId>
<version>6.1.0</version>
<version>6.2.0</version>
</parent>
<artifactId>pigx-flow-api</artifactId>
@@ -0,0 +1,26 @@
package com.pig4cloud.pigx.flow.constant;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/** 流程通知事件类型;编码与 flow_notice.kind 保持兼容,标签用于统一站内信标题。 */
@Getter
@RequiredArgsConstructor
public enum FlowNoticeKindEnum {
/** 新分配的待办任务。 */
TODO("TODO", "待办"),
/** 发起人手动催办。 */
URGE("URGE", "催办"),
/** 审批节点超时提醒。 */
OVERDUE("OVERDUE", "超时提醒");
/** 持久化编码,不能随枚举重命名改变。 */
private final String code;
/** 站内信标题前缀。 */
private final String label;
}
@@ -0,0 +1,34 @@
package com.pig4cloud.pigx.flow.constant;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* 流程通知事件状态;READY 经统一信息推送发送后转为 SENT,待触发提醒随任务结束取消。
*/
@Getter
@RequiredArgsConstructor
public enum FlowNoticeStatusEnum {
/** 等待截止时间,到期后确定当前审批人。 */
PENDING("PENDING"),
/** 等待投递到统一站内信。 */
READY("READY"),
/** 已投递到统一站内信,已读状态由 sys_message_relation 维护。 */
SENT("SENT"),
/** 任务结束或接收人变更,等待删除统一站内信的接收记录。 */
REMOVING("REMOVING"),
/** 任务已经结束,提醒已取消或接收记录已删除。 */
CANCELLED("CANCELLED"),
/** 旧监听没有截止时间,迁移后保留但不参与扫描。 */
DISABLED("DISABLED");
/** 数据库状态值,保持与已发布的三表结构兼容。 */
private final String code;
}
@@ -1,113 +0,0 @@
package com.pig4cloud.pigx.flow.constant;
import cn.hutool.core.util.ObjectUtil;
import java.util.EnumSet;
import java.util.Objects;
/**
* 枚举通用接口
* <p>
* 为所有业务枚举提供统一的接口规范,实现枚举值和标签的标准化管理。
* 提供了根据值获取枚举、根据值获取标签、根据标签获取值等通用方法。
* 所有业务枚举类都应实现此接口,以保证枚举使用的一致性。
* </p>
*
* @param <T> 枚举值的类型,通常为Integer或String
* @author haoxr
* @since 2022/3/27 12:06
*/
public interface IBaseEnum<T> {
/**
* 获取枚举值
* <p>
* 返回枚举对应的实际值,通常用于数据库存储或接口传输
* </p>
*
* @return 枚举对应的值
*/
T getValue();
/**
* 获取枚举标签
* <p>
* 返回枚举对应的显示标签,通常用于界面展示
* </p>
*
* @return 枚举对应的中文标签
*/
String getLabel();
/**
* 根据值获取对应的枚举实例
* <p>
* 通过枚举值查找对应的枚举实例,常用于数据库查询结果的转换。
* 如果找不到匹配的枚举,返回null。
* </p>
*
* @param value 枚举值
* @param clazz 枚举类的Class对象
* @param <E> 枚举类型,必须实现IBaseEnum接口
* @return 匹配的枚举实例,如果没有找到返回null
*/
static <E extends Enum<E> & IBaseEnum> E getEnumByValue(Object value, Class<E> clazz) {
Objects.requireNonNull(value);
EnumSet<E> allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举
E matchEnum = allEnums.stream().filter(e -> ObjectUtil.equal(e.getValue(), value)).findFirst().orElse(null);
return matchEnum;
}
/**
* 根据值获取对应的标签
* <p>
* 通过枚举值直接获取对应的显示标签,常用于列表展示时的值转换。
* 如果找不到匹配的枚举,返回null。
* </p>
*
* @param value 枚举值
* @param clazz 枚举类的Class对象
* @param <E> 枚举类型,必须实现IBaseEnum接口
* @return 枚举对应的标签,如果没有找到返回null
*/
static <E extends Enum<E> & IBaseEnum> String getLabelByValue(Object value, Class<E> clazz) {
Objects.requireNonNull(value);
EnumSet<E> allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举
E matchEnum = allEnums.stream().filter(e -> ObjectUtil.equal(e.getValue(), value)).findFirst().orElse(null);
String label = null;
if (matchEnum != null) {
label = matchEnum.getLabel();
}
return label;
}
/**
* 根据标签获取对应的值
* <p>
* 通过枚举标签反向查找对应的枚举值,常用于表单提交时的数据转换。
* 如果找不到匹配的枚举,返回null。
* </p>
*
* @param label 枚举标签
* @param clazz 枚举类的Class对象
* @param <E> 枚举类型,必须实现IBaseEnum接口
* @return 枚举对应的值,如果没有找到返回null
*/
static <E extends Enum<E> & IBaseEnum> Object getValueByLabel(String label, Class<E> clazz) {
Objects.requireNonNull(label);
EnumSet<E> allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举
String finalLabel = label;
E matchEnum = allEnums.stream()
.filter(e -> ObjectUtil.equal(e.getLabel(), finalLabel))
.findFirst()
.orElse(null);
Object value = null;
if (matchEnum != null) {
value = matchEnum.getValue();
}
return value;
}
}
@@ -60,6 +60,11 @@ public class ProcessInstanceConstant {
*/
public static final String REJECT_TO_STARTER_VAR = "rejectToStarter";
/**
* 流程变量:明确的流程结束原因
*/
public static final String FINISH_REASON_VAR = "_pigx_finish_reason";
/**
* 流程结束原因:拒绝
*/
@@ -0,0 +1,51 @@
package com.pig4cloud.pigx.flow.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
* 个人流程草稿保存请求;租户和用户只能由认证上下文提供。标题和内容仅反序列化,避免操作日志记录个人表单。
*/
@Data
@Schema(description = "个人流程草稿保存请求")
public class FlowDraftDTO {
/** 首次保存为空;更新时为已有草稿 ID。 */
@Schema(description = "首次保存为空,更新使用已有草稿 ID")
private String id;
/** 草稿类型:DESIGN 流程设计,APPLICATION 动态表单申请。 */
@NotBlank
@Pattern(regexp = "DESIGN|APPLICATION", message = "草稿类型无效")
@Schema(description = "草稿类型", allowableValues = { "DESIGN", "APPLICATION" })
private String kind;
/** 关联流程 ID;尚未建立流程的设计草稿为空串。 */
@NotNull
@Size(max = 64)
@Schema(description = "关联流程 ID,尚未建立流程的设计草稿可为空串")
private String flowId;
/** 草稿标题,最多 200 字符。 */
@NotBlank
@Size(max = 200)
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Schema(description = "非空标题,最多 200 字符")
private String title;
/** 与 kind 对应的 JSON 对象字符串,最多 1 MB UTF-8;字节数和 JSON 结构由服务端校验。 */
@NotBlank
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Schema(description = "与 kind 对应的 JSON 对象字符串,最多 1 MB UTF-8")
private String payload;
/** 首次保存为 0;更新时为读取到的版本号,冲突时拒绝覆盖。 */
@Schema(description = "首次保存为 0,更新为读取时的版本号")
private long revision;
}
@@ -0,0 +1,15 @@
package com.pig4cloud.pigx.flow.dto;
import lombok.Data;
/** 打开动态表单时查询字段权限的 REST 配置。 */
@Data
public class FormPermissionConfig {
/** 是否启用 REST 权限查询;null 或 false 表示使用静态权限。 */
private Boolean enabled;
/** 已启用时必填的完整 HTTP(S) 地址,不能包含账号或片段。 */
private String url;
}
@@ -70,6 +70,9 @@ public class Node {
*/
private String eventConfig;
/** 超时提醒分钟数;空或 0 表示关闭,按任务创建时间计算自然时长。 */
private Integer reminderMinutes;
/**
* 执行人分配类型 对应AssignedTypeClass中的常量(指定用户、部门主管、角色等)
*/
@@ -121,11 +124,19 @@ public class Node {
*/
private List<Node> conditionNodes;
/**
* 条件分支是否允许同时命中多条 true=包容网关,满足条件的分支都执行;空或 false=排他网关,只走一条
*/
private Boolean multiMatch;
/**
* 表单字段权限配置 key=表单字段ID,value=权限类型(H=隐藏,R=只读,E=编辑)
*/
private Map<String, String> formPerms;
/** REST 返回值按字段覆盖静态权限;未启用时沿用 formPerms。 */
private FormPermissionConfig formPermissionConfig;
/**
* 无执行人处理配置 定义当节点找不到执行人时的处理策略
*/
@@ -1,40 +0,0 @@
package com.pig4cloud.pigx.flow.dto;
import lombok.Data;
import java.util.Map;
/**
* 节点HTTP请求结果数据传输对象
* <p>
* 该DTO用于封装流程节点执行HTTP请求后的返回结果。
* 在流程执行过程中,某些节点可能需要调用外部系统的接口(如获取审批人、
* 验证业务数据等),此对象用于统一封装这些HTTP请求的响应结果。
* </p>
*
* @author pigx
*/
@Data
public class NodeHttpResultVO {
/**
* 请求是否成功
* <p>
* 标识HTTP请求是否执行成功:
* - true: 请求成功,data字段包含返回的业务数据
* - false: 请求失败,data字段可能包含错误信息
* </p>
*/
private Boolean ok;
/**
* 返回的数据内容
* <p>
* HTTP请求返回的业务数据,以Map形式存储,便于灵活处理各种数据结构。
* 成功时包含业务数据,失败时可能包含错误码、错误信息等。
* 具体内容取决于调用的接口定义。
* </p>
*/
private Map<String, Object> data;
}
@@ -0,0 +1,22 @@
package com.pig4cloud.pigx.flow.dto;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.util.Map;
/** 打开发起表单;initialData 为草稿与业务入口合并后的初始数据。 */
@Data
public class OpenProcessFormDto {
/** 待发起流程的业务标识,必填。 */
@NotBlank
private String flowId;
/** 可选初始数据,由调用方按草稿低于入口参数的优先级合并,覆盖表单默认值。 */
private Map<String, Object> initialData;
/** 恢复草稿时校验发布版本,防止将旧稿数据送给新版本的权限接口。 */
private String draftVersion;
}
Loaded 100 of 397 files, more files were not shown because too many files have changed in this diff. Show more