diff --git a/db/20260920_add_wallet_card_template.sql b/db/20260920_add_wallet_card_template.sql new file mode 100644 index 00000000..20f3481d --- /dev/null +++ b/db/20260920_add_wallet_card_template.sql @@ -0,0 +1,48 @@ +-- 饭卡卡面打印:卡面模板表 + 默认种子模板。 +-- 本脚本只新增表和种子数据,不修改既有表结构,可安全重跑。 +-- 菜单与按钮权限见同目录 20260920_饭卡卡面模板菜单权限.txt,由管理员手工添加。 +-- 模板 JSON 由前端可视化设计器生成与消费:background(背景) + elements(元素数组), +-- 元素坐标基于 1014×642 @300dpi 卡面(HiTi CS-2xx 标准),文本 content 支持 {{field}} 动态字段插值。 + +CREATE TABLE IF NOT EXISTS `pjcloudx_wallet`.`wallet_card_template` +( + `id` bigint NOT NULL COMMENT '主键', + `name` varchar(64) NOT NULL COMMENT '模板名称', + `identity_type` varchar(16) DEFAULT NULL COMMENT '适用身份:STUDENT学生/TEACHER教师,空为通用', + `sides` char(1) NOT NULL DEFAULT '1' COMMENT '打印面数:1单面 2双面', + `front_json` mediumtext COMMENT '正面设计JSON(背景+元素数组)', + `back_json` mediumtext COMMENT '背面设计JSON(背景+元素数组),单面模板为空', + `remark` varchar(255) DEFAULT NULL COMMENT '备注', + `create_by` varchar(64) DEFAULT NULL COMMENT '创建人', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` varchar(64) DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + `del_flag` char(1) DEFAULT '0' COMMENT '删除标记', + `tenant_id` bigint DEFAULT NULL COMMENT '租户ID', + PRIMARY KEY (`id`), + KEY `idx_wallet_card_template_tenant` (`tenant_id`, `identity_type`, `del_flag`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci + COMMENT = '饭卡卡面打印模板'; + +-- 种子模板:为所有已初始化字典(即已开通)的租户各插一套学生/教师默认模板,按租户+名称幂等。 +INSERT INTO `pjcloudx_wallet`.`wallet_card_template` +(`id`, `name`, `identity_type`, `sides`, `front_json`, `back_json`, `remark`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) +SELECT (SELECT GREATEST(COALESCE(MAX(`id`), 0), 2026092000000000000) FROM `pjcloudx_wallet`.`wallet_card_template`) + ROW_NUMBER() OVER (ORDER BY tenant.`tenant_id`, item.`sort`), + item.`name`, item.`identity_type`, item.`sides`, item.`front_json`, item.`back_json`, item.`remark`, + 'system', NOW(), 'system', NOW(), '0', tenant.`tenant_id` +FROM (SELECT DISTINCT `tenant_id` FROM `pjcloudx`.`sys_dict` WHERE `del_flag` = '0') tenant +JOIN ( + SELECT '学生饭卡' AS `name`, 'STUDENT' AS `identity_type`, '2' AS `sides`, 1 AS `sort`, + '学生卡默认模板:姓名/学号/班级/头像' AS `remark`, + '{"background":{"type":"gradient","color":"#4e6ef2","color2":"#2d3a8c","angle":135},"elements":[{"id":"s1","type":"text","x":72,"y":64,"w":560,"h":96,"content":"{{name}}","fontSize":76,"bold":true,"color":"#ffffff","fontFamily":"PingFang SC","align":"left","lineHeight":1.2,"opacity":1,"rotate":0,"resin":false},{"id":"s2","type":"text","x":72,"y":208,"w":560,"h":56,"content":"学号:{{studentNo}}","fontSize":40,"bold":false,"color":"#ffffff","fontFamily":"PingFang SC","align":"left","lineHeight":1.2,"opacity":1,"rotate":0,"resin":false},{"id":"s3","type":"text","x":72,"y":284,"w":560,"h":56,"content":"班级:{{className}}","fontSize":40,"bold":false,"color":"#ffffff","fontFamily":"PingFang SC","align":"left","lineHeight":1.2,"opacity":1,"rotate":0,"resin":false},{"id":"s4","type":"image","x":724,"y":84,"w":216,"h":288,"src":"{{avatar}}","fit":"cover","radius":12,"opacity":1,"rotate":0},{"id":"s5","type":"rect","x":0,"y":492,"w":1014,"h":150,"fill":"rgba(255,255,255,0.14)","radius":0,"opacity":1,"rotate":0},{"id":"s6","type":"text","x":72,"y":545,"w":600,"h":48,"content":"智慧食堂 · 饭卡","fontSize":34,"bold":false,"color":"#ffffff","fontFamily":"PingFang SC","align":"left","lineHeight":1.2,"opacity":1,"rotate":0,"resin":false}]}' AS `front_json`, + '{"background":{"type":"gradient","color":"#2d3a8c","color2":"#4e6ef2","angle":135},"elements":[{"id":"b1","type":"text","x":72,"y":72,"w":400,"h":64,"content":"使用须知","fontSize":48,"bold":true,"color":"#ffffff","fontFamily":"PingFang SC","align":"left","lineHeight":1.2,"opacity":1,"rotate":0,"resin":false},{"id":"b2","type":"text","x":72,"y":170,"w":870,"h":250,"content":"1. 本卡仅限本人使用,请勿转借;\n2. 卡片遗失请及时到管理中心挂失补办;\n3. 请保持卡面清洁,避免弯折、靠近高温。","fontSize":30,"bold":false,"color":"rgba(255,255,255,0.92)","fontFamily":"PingFang SC","align":"left","lineHeight":1.5,"opacity":1,"rotate":0,"resin":false},{"id":"b3","type":"rect","x":0,"y":492,"w":1014,"h":150,"fill":"rgba(255,255,255,0.14)","radius":0,"opacity":1,"rotate":0},{"id":"b4","type":"text","x":72,"y":545,"w":600,"h":48,"content":"卡号:{{cardNumber}}","fontSize":34,"bold":false,"color":"#ffffff","fontFamily":"PingFang SC","align":"left","lineHeight":1.2,"opacity":1,"rotate":0,"resin":false}]}' AS `back_json` + UNION ALL + SELECT '教师饭卡', 'TEACHER', '1', 2, + '教师卡默认模板:校徽/标语/姓名/工号', + '{"background":{"type":"gradient","color":"#0f766e","color2":"#134e4a","angle":135},"elements":[{"id":"t1","type":"image","x":72,"y":64,"w":120,"h":120,"src":"","fit":"contain","radius":0,"opacity":1,"rotate":0},{"id":"t2","type":"text","x":216,"y":92,"w":640,"h":72,"content":"智慧食堂 · 教师卡","fontSize":52,"bold":true,"color":"#ffffff","fontFamily":"PingFang SC","align":"left","lineHeight":1.2,"opacity":1,"rotate":0,"resin":false},{"id":"t3","type":"text","x":72,"y":248,"w":560,"h":64,"content":"{{name}}","fontSize":48,"bold":true,"color":"#ffffff","fontFamily":"PingFang SC","align":"left","lineHeight":1.2,"opacity":1,"rotate":0,"resin":false},{"id":"t4","type":"text","x":72,"y":336,"w":560,"h":56,"content":"工号:{{teacherNo}}","fontSize":40,"bold":false,"color":"#ffffff","fontFamily":"PingFang SC","align":"left","lineHeight":1.2,"opacity":1,"rotate":0,"resin":false},{"id":"t5","type":"rect","x":0,"y":492,"w":1014,"h":150,"fill":"rgba(255,255,255,0.14)","radius":0,"opacity":1,"rotate":0},{"id":"t6","type":"text","x":72,"y":545,"w":640,"h":48,"content":"一餐一饭 皆是心意","fontSize":34,"bold":false,"color":"#ffffff","fontFamily":"PingFang SC","align":"left","lineHeight":1.2,"opacity":1,"rotate":0,"resin":false}]}' AS `front_json`, + NULL AS `back_json` +) item ON item.`name` IS NOT NULL +WHERE NOT EXISTS ( + SELECT 1 FROM `pjcloudx_wallet`.`wallet_card_template` t + WHERE t.`tenant_id` = tenant.`tenant_id` AND t.`name` = item.`name` AND t.`del_flag` = '0' +); diff --git a/db/20260920_饭卡卡面模板菜单权限.txt b/db/20260920_饭卡卡面模板菜单权限.txt new file mode 100644 index 00000000..8a92d424 --- /dev/null +++ b/db/20260920_饭卡卡面模板菜单权限.txt @@ -0,0 +1,92 @@ +======================================================================== +饭卡卡面打印 - 菜单/权限手工添加清单 (2026-09-20) +======================================================================== + +说明:以下内容需要管理员在对应环境手工添加(UPMS 管理端「菜单管理」或直接执行 SQL)。 +SQL 按租户幂等,风格与 db/20260918_hard_coffee_dict_menu.txt 一致。 +菜单 parent_id 依赖各环境实际菜单主键:模板页与「钱包开卡」菜单同级 +(参照 permission='wallet_walletActive_view' 的菜单取父级),打印按钮挂在「钱包开卡」页面菜单下。 + +前端页面: + 卡面模板管理 src/views/wallet/card-template/index.vue(内嵌可视化设计器) + 打印入口 src/views/wallet/wallet-active/index.vue 操作列/工具栏(本机打印助手配合) + +------------------------------------------------------------------------ +一、页面菜单:卡面模板(与钱包开卡同级) +------------------------------------------------------------------------ + +INSERT INTO `pjcloudx`.`sys_menu` +(`menu_id`, `name`, `permission`, `path`, `component`, `parent_id`, `icon`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) +SELECT (SELECT GREATEST(COALESCE(MAX(`menu_id`), 0), 2026092000000000000) FROM `pjcloudx`.`sys_menu`) + ROW_NUMBER() OVER (ORDER BY sibling.`tenant_id`, sibling.`menu_id`), + '卡面模板', 'wallet_cardTemplate_view', '/wallet/card-template', 'wallet/card-template/index', + sibling.`parent_id`, NULL, '1', 50, '0', '0', '0', + 'system', NOW(), 'system', NOW(), '0', sibling.`tenant_id` +FROM `pjcloudx`.`sys_menu` sibling +WHERE sibling.`permission` = 'wallet_walletActive_view' AND sibling.`del_flag` = '0' + AND NOT EXISTS ( + SELECT 1 FROM `pjcloudx`.`sys_menu` existing + WHERE existing.`tenant_id` = sibling.`tenant_id` + AND existing.`permission` = 'wallet_cardTemplate_view' AND existing.`del_flag` = '0' + ); + +------------------------------------------------------------------------ +二、按钮权限:卡面模板 新增/编辑/删除(挂在卡面模板页面菜单下) +------------------------------------------------------------------------ + +INSERT INTO `pjcloudx`.`sys_menu` +(`menu_id`, `name`, `permission`, `parent_id`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `del_flag`, `tenant_id`) +SELECT (SELECT GREATEST(COALESCE(MAX(`menu_id`), 0), 2026092000000000000) FROM `pjcloudx`.`sys_menu`) + ROW_NUMBER() OVER (ORDER BY page_menu.`tenant_id`, page_menu.`menu_id`, item.`sort`), + item.`name`, item.`permission`, page_menu.`menu_id`, + '1', item.`sort`, '0', '0', '2', 'system', NOW(), '0', page_menu.`tenant_id` +FROM `pjcloudx`.`sys_menu` page_menu +JOIN ( + SELECT '新增模板' AS `name`, 'wallet_cardTemplate_add' AS `permission`, 1 AS `sort` + UNION ALL SELECT '编辑模板', 'wallet_cardTemplate_edit', 2 + UNION ALL SELECT '删除模板', 'wallet_cardTemplate_del', 3 +) item ON item.`name` IS NOT NULL +WHERE page_menu.`permission` = 'wallet_cardTemplate_view' AND page_menu.`menu_type` = '0' AND page_menu.`del_flag` = '0' + AND NOT EXISTS ( + SELECT 1 FROM `pjcloudx`.`sys_menu` existing + WHERE existing.`tenant_id` = page_menu.`tenant_id` + AND existing.`permission` = item.`permission` AND existing.`del_flag` = '0' + ); + +------------------------------------------------------------------------ +三、按钮权限:钱包开卡页「打印卡面」(挂在钱包开卡页面菜单下) +------------------------------------------------------------------------ + +INSERT INTO `pjcloudx`.`sys_menu` +(`menu_id`, `name`, `permission`, `parent_id`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `del_flag`, `tenant_id`) +SELECT (SELECT GREATEST(COALESCE(MAX(`menu_id`), 0), 2026092000000000000) FROM `pjcloudx`.`sys_menu`) + ROW_NUMBER() OVER (ORDER BY page_menu.`tenant_id`, page_menu.`menu_id`), + '打印卡面', 'wallet_walletActive_print', page_menu.`menu_id`, + '1', 30, '0', '0', '2', 'system', NOW(), '0', page_menu.`tenant_id` +FROM `pjcloudx`.`sys_menu` page_menu +WHERE page_menu.`permission` = 'wallet_walletActive_view' AND page_menu.`menu_type` = '0' AND page_menu.`del_flag` = '0' + AND NOT EXISTS ( + SELECT 1 FROM `pjcloudx`.`sys_menu` existing + WHERE existing.`tenant_id` = page_menu.`tenant_id` + AND existing.`permission` = 'wallet_walletActive_print' AND existing.`del_flag` = '0' + ); + +------------------------------------------------------------------------ +四、后端权限字符串与接口对应关系(前端页面取用) +------------------------------------------------------------------------ + +wallet_cardTemplate_view: + GET /wallet/card-template/page 卡面模板分页(管理页) + +wallet_cardTemplate_add: + POST /wallet/card-template 新增卡面模板 + +wallet_cardTemplate_edit: + PUT /wallet/card-template 修改卡面模板 + +wallet_cardTemplate_del: + DELETE /wallet/card-template 删除卡面模板 + +登录即可(无权限注解,打印抽屉取模板用): + GET /wallet/card-template/list 卡面模板全量列表 + GET /wallet/card-template/{id} 卡面模板详情 + +wallet_walletActive_print: + 前端按钮权限(v-auth),不对应后端接口;打印数据直接复用开卡列表行数据。 diff --git a/pjcloud-wallet/pjcloud-wallet-api/src/main/java/com/bsd/pjcloud/wallet/api/entity/WalletCardTemplateEntity.java b/pjcloud-wallet/pjcloud-wallet-api/src/main/java/com/bsd/pjcloud/wallet/api/entity/WalletCardTemplateEntity.java new file mode 100644 index 00000000..5ade12e8 --- /dev/null +++ b/pjcloud-wallet/pjcloud-wallet-api/src/main/java/com/bsd/pjcloud/wallet/api/entity/WalletCardTemplateEntity.java @@ -0,0 +1,113 @@ +package com.bsd.pjcloud.wallet.api.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import com.bsd.pjcloud.common.core.util.TenantTable; +import java.time.LocalDateTime; + +/** + * 饭卡卡面打印模板 + * + *
模板为前端可视化设计器产出的结构化 JSON(元素坐标基于 1014×642 @300dpi 卡面), + * 打印时由前端按开卡记录填充动态字段后渲染成图,后端只负责存取。
+ * + * @author dzc + * @date 2026-09-20 + */ +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Data +@TenantTable +@TableName("wallet_card_template") +@EqualsAndHashCode(callSuper = true) +@Schema(description = "饭卡卡面打印模板") +public class WalletCardTemplateEntity extends Model模板 JSON 由前端可视化设计器生成与消费;list/detail 登录即可访问, + * 供开卡打印抽屉在无模板管理权限时也能取模板。
+ * + * @author dzc + * @date 2026-09-20 + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/card-template") +@Tag(description = "card-template", name = "饭卡卡面打印模板") +@SecurityRequirement(name = HttpHeaders.AUTHORIZATION) +@Validated +public class WalletCardTemplateController { + + private final WalletCardTemplateService walletCardTemplateService; + + /** + * 模板全量列表(按更新时间倒序),打印抽屉与设计器下拉使用,登录即可访问。 + */ + @GetMapping("/list") + @Operation(summary = "卡面模板列表", description = "登录即可访问,供打印抽屉选择模板") + public R