ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/dto/ChargingCardBalanceDTO.java
New file @@ -0,0 +1,31 @@ package com.ruoyi.account.api.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; import java.math.BigDecimal; import java.util.List; @Data @ApiModel(value = "充电卡余额更新参数") public class ChargingCardBalanceDTO implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "卡片ID集合", required = true) private List<Long> ids; @ApiModelProperty(value = "调整类型:1增加,2减少", required = true) private Integer type; @ApiModelProperty(value = "调整金额", required = true) private BigDecimal amount; @ApiModelProperty(value = "备注") private String remark; @ApiModelProperty(value = "操作密码", required = true) private String operationPassword; } ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/dto/ChargingCardExcelDTO.java
New file @@ -0,0 +1,24 @@ package com.ruoyi.account.api.dto; import com.ruoyi.common.core.annotation.Excel; import io.swagger.annotations.ApiModel; import lombok.Data; import java.io.Serializable; import java.math.BigDecimal; @Data @ApiModel(value = "充电卡导入导出数据") public class ChargingCardExcelDTO implements Serializable { private static final long serialVersionUID = 1L; @Excel(name = "卡号", width = 22, prompt = "必填,最多64个字符") private String cardNo; @Excel(name = "卡余额", width = 16, scale = 2, prompt = "选填,必须大于等于0") private BigDecimal balance; @Excel(name = "启用状态", width = 14, readConverterExp = "1=启用,2=禁用", combo = {"启用", "禁用"}) private String status; } ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/dto/ChargingCardSaveDTO.java
New file @@ -0,0 +1,27 @@ package com.ruoyi.account.api.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; import java.math.BigDecimal; @Data @ApiModel(value = "充电卡保存参数") public class ChargingCardSaveDTO implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "主键ID,编辑时必填") private Long id; @ApiModelProperty(value = "卡号", required = true) private String cardNo; @ApiModelProperty(value = "卡余额") private BigDecimal balance; @ApiModelProperty(value = "状态:1启用,2禁用") private Integer status; } ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/dto/ChargingCardStatusDTO.java
New file @@ -0,0 +1,20 @@ package com.ruoyi.account.api.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.io.Serializable; @Data @ApiModel(value = "充电卡状态更新参数") public class ChargingCardStatusDTO implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "卡片ID", required = true) private Long id; @ApiModelProperty(value = "状态:1启用,2禁用", required = true) private Integer status; } ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/TChargingCard.java
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import com.ruoyi.common.core.annotation.Excel; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -24,16 +25,19 @@ @ApiModelProperty(value = "主键ID") private Long id; @Excel(name = "卡号", width = 22) @ApiModelProperty(value = "卡号") private String cardNo; @Excel(name = "卡余额", width = 16, scale = 2) @ApiModelProperty(value = "卡余额") private BigDecimal balance; @Excel(name = "启用状态", width = 14, readConverterExp = "1=启用,2=禁用") @ApiModelProperty(value = "状态:1启用,2禁用") private Integer status; @TableLogic @TableLogic(value = "0", delval = "2") @ApiModelProperty(value = "删除状态:0正常,2删除") private Integer delFlag; ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TChargingCardController.java
@@ -1,20 +1,33 @@ package com.ruoyi.account.controller; import com.baomidou.mybatisplus.core.metadata.IPage; import com.ruoyi.account.api.dto.ChargingCardBalanceDTO; import com.ruoyi.account.api.dto.ChargingCardExcelDTO; import com.ruoyi.account.api.dto.ChargingCardQueryDTO; import com.ruoyi.account.api.dto.ChargingCardSaveDTO; import com.ruoyi.account.api.dto.ChargingCardStatusDTO; import com.ruoyi.account.api.model.TChargingCard; import com.ruoyi.account.service.TChargingCardService; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.utils.poi.ExcelUtil; import com.ruoyi.common.core.web.controller.BaseController; import com.ruoyi.common.security.annotation.Logical; import com.ruoyi.common.security.annotation.RequiresPermissions; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.util.List; @Api(tags = "卡片管理") @RestController @@ -30,4 +43,64 @@ public R<IPage<TChargingCard>> page(@RequestBody ChargingCardQueryDTO query) { return R.ok(chargingCardService.selectCardPage(query)); } @ApiOperation(value = "新增卡片") @RequiresPermissions(value = {"/chargingCard/add"}, logical = Logical.OR) @PostMapping public R<Boolean> add(@RequestBody ChargingCardSaveDTO dto) { return R.ok(chargingCardService.saveCard(dto)); } @ApiOperation(value = "编辑卡片") @RequiresPermissions(value = {"/chargingCard/edit"}, logical = Logical.OR) @PutMapping public R<Boolean> edit(@RequestBody ChargingCardSaveDTO dto) { return R.ok(chargingCardService.updateCard(dto)); } @ApiOperation(value = "删除卡片") @RequiresPermissions(value = {"/chargingCard/delete"}, logical = Logical.OR) @DeleteMapping public R<Boolean> delete(@RequestBody List<Long> ids) { return R.ok(chargingCardService.deleteCards(ids)); } @ApiOperation(value = "更新卡片启用状态") @RequiresPermissions(value = {"/chargingCard/status"}, logical = Logical.OR) @PutMapping("/status") public R<Boolean> status(@RequestBody ChargingCardStatusDTO dto) { return R.ok(chargingCardService.updateStatus(dto)); } @ApiOperation(value = "批量更新卡片余额") @RequiresPermissions(value = {"/chargingCard/balance"}, logical = Logical.OR) @PutMapping("/balance") public R<Boolean> balance(@RequestBody ChargingCardBalanceDTO dto) { return R.ok(chargingCardService.updateBalance(dto)); } @ApiOperation(value = "导出卡片") @RequiresPermissions(value = {"/chargingCard/export"}, logical = Logical.OR) @PutMapping("/export") public void export(HttpServletResponse response, @RequestBody ChargingCardQueryDTO query) { ExcelUtil<ChargingCardExcelDTO> util = new ExcelUtil<>(ChargingCardExcelDTO.class); util.exportExcel(response, chargingCardService.selectExportList(query), "卡片数据"); } @ApiOperation(value = "下载卡片导入模板") @RequiresPermissions(value = {"/chargingCard/import"}, logical = Logical.OR) @GetMapping("/import-template") public void importTemplate(HttpServletResponse response) { ExcelUtil<ChargingCardExcelDTO> util = new ExcelUtil<>(ChargingCardExcelDTO.class); util.importTemplateExcel(response, "卡片数据"); } @ApiOperation(value = "导入卡片") @RequiresPermissions(value = {"/chargingCard/import"}, logical = Logical.OR) @PostMapping("/import") public R<String> importData(@RequestPart("file") MultipartFile file) throws Exception { ExcelUtil<ChargingCardExcelDTO> util = new ExcelUtil<>(ChargingCardExcelDTO.class); List<ChargingCardExcelDTO> cards = util.importExcel(file.getInputStream()); return R.ok(chargingCardService.importCards(cards)); } } ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/mapper/TChargingCardMapper.java
@@ -3,12 +3,32 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.account.api.dto.ChargingCardExcelDTO; import com.ruoyi.account.api.dto.ChargingCardQueryDTO; import com.ruoyi.account.api.model.TChargingCard; import org.apache.ibatis.annotations.Param; import java.math.BigDecimal; import java.util.List; public interface TChargingCardMapper extends BaseMapper<TChargingCard> { IPage<TChargingCard> selectCardPage(Page<TChargingCard> page, @Param("query") ChargingCardQueryDTO query); List<ChargingCardExcelDTO> selectExportList(@Param("query") ChargingCardQueryDTO query); int countByCardNo(@Param("cardNo") String cardNo, @Param("excludeId") Long excludeId); int adjustBalance(@Param("ids") List<Long> ids, @Param("amount") BigDecimal amount, @Param("type") Integer type, @Param("maxAmount") BigDecimal maxAmount, @Param("updateBy") Long updateBy); int insertBalanceRecord(@Param("cardId") Long cardId, @Param("type") Integer type, @Param("amount") BigDecimal amount, @Param("remark") String remark, @Param("createBy") Long createBy); } ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/TChargingCardService.java
@@ -2,10 +2,30 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.account.api.dto.ChargingCardBalanceDTO; import com.ruoyi.account.api.dto.ChargingCardExcelDTO; import com.ruoyi.account.api.dto.ChargingCardQueryDTO; import com.ruoyi.account.api.dto.ChargingCardSaveDTO; import com.ruoyi.account.api.dto.ChargingCardStatusDTO; import com.ruoyi.account.api.model.TChargingCard; import java.util.List; public interface TChargingCardService extends IService<TChargingCard> { IPage<TChargingCard> selectCardPage(ChargingCardQueryDTO query); boolean saveCard(ChargingCardSaveDTO dto); boolean updateCard(ChargingCardSaveDTO dto); boolean deleteCards(List<Long> ids); boolean updateStatus(ChargingCardStatusDTO dto); boolean updateBalance(ChargingCardBalanceDTO dto); List<ChargingCardExcelDTO> selectExportList(ChargingCardQueryDTO query); String importCards(List<ChargingCardExcelDTO> cards); } ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/TChargingCardServiceImpl.java
@@ -3,19 +3,283 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.account.api.dto.ChargingCardBalanceDTO; import com.ruoyi.account.api.dto.ChargingCardExcelDTO; import com.ruoyi.account.api.dto.ChargingCardQueryDTO; import com.ruoyi.account.api.dto.ChargingCardSaveDTO; import com.ruoyi.account.api.dto.ChargingCardStatusDTO; import com.ruoyi.account.api.model.TChargingCard; import com.ruoyi.account.mapper.TChargingCardMapper; import com.ruoyi.account.service.TChargingCardService; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.exception.ServiceException; import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.security.service.TokenService; import com.ruoyi.common.security.utils.SecurityUtils; import com.ruoyi.system.api.domain.SysUser; import com.ruoyi.system.api.feignClient.SysUserClient; import com.ruoyi.system.api.model.LoginUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.HashSet; import java.util.List; import java.util.Set; @Service public class TChargingCardServiceImpl extends ServiceImpl<TChargingCardMapper, TChargingCard> implements TChargingCardService { private static final int ENABLED = 1; private static final int DISABLED = 2; private static final int BALANCE_ADD = 1; private static final int BALANCE_SUBTRACT = 2; private static final BigDecimal MAX_AMOUNT = new BigDecimal("9999999999.99"); @Autowired private TokenService tokenService; @Autowired private SysUserClient sysUserClient; @Override public IPage<TChargingCard> selectCardPage(ChargingCardQueryDTO query) { Page<TChargingCard> page = new Page<>(query.getPageCurr(), query.getPageSize()); return baseMapper.selectCardPage(page, query); ChargingCardQueryDTO safeQuery = query == null ? new ChargingCardQueryDTO() : query; long pageNum = safeQuery.getPageCurr() == null || safeQuery.getPageCurr() < 1 ? 1 : safeQuery.getPageCurr(); long pageSize = safeQuery.getPageSize() == null || safeQuery.getPageSize() < 1 ? 10 : safeQuery.getPageSize(); Page<TChargingCard> page = new Page<>(pageNum, pageSize); return baseMapper.selectCardPage(page, safeQuery); } @Override public boolean saveCard(ChargingCardSaveDTO dto) { validateCard(dto, false); TChargingCard card = new TChargingCard(); card.setCardNo(dto.getCardNo().trim()); card.setBalance(dto.getBalance() == null ? BigDecimal.ZERO : dto.getBalance()); card.setStatus(dto.getStatus() == null ? ENABLED : dto.getStatus()); card.setDelFlag(0); card.setCreateTime(LocalDateTime.now()); card.setCreateBy(SecurityUtils.getUserId()); if (!save(card)) { throw new ServiceException("新增卡片失败"); } return true; } @Override public boolean updateCard(ChargingCardSaveDTO dto) { validateCard(dto, true); if (getById(dto.getId()) == null) { throw new ServiceException("卡片不存在或已删除"); } TChargingCard card = new TChargingCard(); card.setId(dto.getId()); card.setCardNo(dto.getCardNo().trim()); card.setStatus(dto.getStatus() == null ? ENABLED : dto.getStatus()); card.setUpdateTime(LocalDateTime.now()); card.setUpdateBy(SecurityUtils.getUserId()); if (!updateById(card)) { throw new ServiceException("修改卡片失败"); } return true; } @Override public boolean deleteCards(List<Long> ids) { if (ids == null || ids.isEmpty()) { throw new ServiceException("请选择需要删除的卡片"); } List<Long> distinctIds = new java.util.ArrayList<>(new HashSet<>(ids)); if (listByIds(distinctIds).size() != distinctIds.size()) { throw new ServiceException("部分卡片不存在或已删除"); } if (!removeByIds(distinctIds)) { throw new ServiceException("删除卡片失败"); } return true; } @Override public boolean updateStatus(ChargingCardStatusDTO dto) { if (dto == null || dto.getId() == null || !isValidStatus(dto.getStatus())) { throw new ServiceException("卡片状态参数错误"); } if (getById(dto.getId()) == null) { throw new ServiceException("卡片不存在或已删除"); } TChargingCard card = new TChargingCard(); card.setId(dto.getId()); card.setStatus(dto.getStatus()); card.setUpdateTime(LocalDateTime.now()); card.setUpdateBy(SecurityUtils.getUserId()); if (!updateById(card)) { throw new ServiceException("更新卡片状态失败"); } return true; } @Override @Transactional(rollbackFor = Exception.class) public boolean updateBalance(ChargingCardBalanceDTO dto) { validateBalanceRequest(dto); verifyOperationPassword(dto.getOperationPassword()); List<TChargingCard> cards = listByIds(dto.getIds()); if (cards.size() != new HashSet<>(dto.getIds()).size()) { throw new ServiceException("部分卡片不存在或已删除"); } int updated = baseMapper.adjustBalance(dto.getIds(), dto.getAmount(), dto.getType(), MAX_AMOUNT, SecurityUtils.getUserId()); if (updated != cards.size()) { throw new ServiceException(dto.getType() == BALANCE_SUBTRACT ? "部分卡片余额不足,无法完成扣减" : "部分卡片余额超过上限,无法完成增加"); } for (TChargingCard card : cards) { baseMapper.insertBalanceRecord(card.getId(), dto.getType(), dto.getAmount(), StringUtils.isEmpty(dto.getRemark()) ? null : dto.getRemark().trim(), SecurityUtils.getUserId()); } return true; } @Override public List<ChargingCardExcelDTO> selectExportList(ChargingCardQueryDTO query) { return baseMapper.selectExportList(query == null ? new ChargingCardQueryDTO() : query); } @Override @Transactional(rollbackFor = Exception.class) public String importCards(List<ChargingCardExcelDTO> cards) { if (cards == null || cards.isEmpty()) { throw new ServiceException("导入数据不能为空"); } Set<String> importCardNos = new HashSet<>(); int row = 1; for (ChargingCardExcelDTO item : cards) { row++; if (item == null || StringUtils.isEmpty(item.getCardNo())) { throw new ServiceException("第" + row + "行卡号不能为空"); } String cardNo = item.getCardNo().trim(); if (cardNo.length() > 64) { throw new ServiceException("第" + row + "行卡号不能超过64个字符"); } if (!importCardNos.add(cardNo)) { throw new ServiceException("第" + row + "行卡号在文件中重复"); } if (existsCardNo(cardNo, null)) { throw new ServiceException("第" + row + "行卡号已存在"); } if (item.getBalance() != null) { validateAmount(item.getBalance(), true, "第" + row + "行卡余额"); } parseExcelStatus(item.getStatus(), row); } Long userId = SecurityUtils.getUserId(); LocalDateTime now = LocalDateTime.now(); for (ChargingCardExcelDTO item : cards) { TChargingCard card = new TChargingCard(); card.setCardNo(item.getCardNo().trim()); card.setBalance(item.getBalance() == null ? BigDecimal.ZERO : item.getBalance()); card.setStatus(parseExcelStatus(item.getStatus(), 0)); card.setDelFlag(0); card.setCreateTime(now); card.setCreateBy(userId); save(card); } return "成功导入" + cards.size() + "条卡片数据"; } private void validateCard(ChargingCardSaveDTO dto, boolean update) { if (dto == null || StringUtils.isEmpty(dto.getCardNo())) { throw new ServiceException("卡号不能为空"); } if (update && dto.getId() == null) { throw new ServiceException("卡片ID不能为空"); } String cardNo = dto.getCardNo().trim(); if (cardNo.length() > 64) { throw new ServiceException("卡号不能超过64个字符"); } if (!update && dto.getBalance() != null) { validateAmount(dto.getBalance(), true, "卡余额"); } if (dto.getStatus() != null && !isValidStatus(dto.getStatus())) { throw new ServiceException("卡片状态参数错误"); } if (existsCardNo(cardNo, dto.getId())) { throw new ServiceException("卡号已存在"); } } private boolean existsCardNo(String cardNo, Long excludeId) { return baseMapper.countByCardNo(cardNo, excludeId) > 0; } private void validateBalanceRequest(ChargingCardBalanceDTO dto) { if (dto == null || dto.getIds() == null || dto.getIds().isEmpty()) { throw new ServiceException("请选择需要更新余额的卡片"); } if (dto.getType() == null || (dto.getType() != BALANCE_ADD && dto.getType() != BALANCE_SUBTRACT)) { throw new ServiceException("余额调整类型错误"); } validateAmount(dto.getAmount(), false, "调整金额"); if (dto.getRemark() != null && dto.getRemark().trim().length() > 255) { throw new ServiceException("备注不能超过255个字符"); } if (StringUtils.isEmpty(dto.getOperationPassword())) { throw new ServiceException("请输入操作密码"); } } private void validateAmount(BigDecimal amount, boolean allowZero, String fieldName) { if (amount == null || (allowZero ? amount.compareTo(BigDecimal.ZERO) < 0 : amount.compareTo(BigDecimal.ZERO) <= 0)) { throw new ServiceException(fieldName + (allowZero ? "不能小于0" : "必须大于0")); } if (amount.scale() > 2) { throw new ServiceException(fieldName + "最多保留2位小数"); } if (amount.compareTo(MAX_AMOUNT) > 0) { throw new ServiceException(fieldName + "不能超过" + MAX_AMOUNT.toPlainString()); } } private Integer parseExcelStatus(String status, int row) { if (StringUtils.isEmpty(status)) { return ENABLED; } String value = status.trim(); if ("1".equals(value) || "启用".equals(value)) { return ENABLED; } if ("2".equals(value) || "禁用".equals(value)) { return DISABLED; } String prefix = row > 0 ? "第" + row + "行" : ""; throw new ServiceException(prefix + "启用状态错误"); } private void verifyOperationPassword(String operationPassword) { LoginUser loginUser = tokenService.getLoginUser(); if (loginUser == null || loginUser.getUserid() == null) { throw new ServiceException("登录状态已失效,请重新登录"); } R<SysUser> response = sysUserClient.getSysUser(loginUser.getUserid()); SysUser user = response == null ? null : response.getData(); if (user == null || StringUtils.isEmpty(user.getOperationPassword())) { throw new ServiceException("当前账号未设置操作密码"); } if (!SecurityUtils.matchesPassword(operationPassword, user.getOperationPassword())) { throw new ServiceException("操作密码错误"); } } private boolean isValidStatus(Integer status) { return status != null && (status == ENABLED || status == DISABLED); } } ruoyi-service/ruoyi-account/src/main/resources/mapper/account/TChargingCardMapper.xml
@@ -25,4 +25,59 @@ ORDER BY create_time DESC, id DESC </select> <select id="selectExportList" resultType="com.ruoyi.account.api.dto.ChargingCardExcelDTO"> SELECT card_no, balance, status FROM t_charging_card WHERE del_flag = 0 <if test="query.cardNo != null and query.cardNo != ''"> AND card_no LIKE CONCAT('%', #{query.cardNo}, '%') </if> <if test="query.status != null"> AND status = #{query.status} </if> ORDER BY create_time DESC, id DESC </select> <select id="countByCardNo" resultType="int"> SELECT COUNT(1) FROM t_charging_card WHERE card_no = #{cardNo} <if test="excludeId != null"> AND id != #{excludeId} </if> </select> <update id="adjustBalance"> UPDATE t_charging_card SET balance = balance <choose> <when test="type == 1">+ #{amount}</when> <otherwise>- #{amount}</otherwise> </choose>, update_time = NOW(), update_by = #{updateBy} WHERE del_flag = 0 AND id IN <foreach collection="ids" item="id" open="(" separator="," close=")"> #{id} </foreach> <choose> <when test="type == 1"> AND balance <= #{maxAmount} - #{amount} </when> <otherwise> AND balance >= #{amount} </otherwise> </choose> </update> <insert id="insertBalanceRecord"> INSERT INTO t_charging_card_balance_record (card_id, change_type, amount, remark, create_time, create_by) VALUES (#{cardId}, #{type}, #{amount}, #{remark}, NOW(), #{createBy}) </insert> </mapper> sql/20260827_charging_card_operations.sql
New file @@ -0,0 +1,54 @@ -- 卡片管理操作功能增量脚本 -- 先在 charging_pile_account 逻辑库执行余额变更记录表,再在 charging_pile_other 逻辑库执行菜单权限。 USE charging_pile_account; CREATE TABLE IF NOT EXISTS `t_charging_card_balance_record` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', `card_id` bigint NOT NULL COMMENT '卡片ID', `change_type` tinyint NOT NULL COMMENT '变更类型:1增加,2减少', `amount` decimal(12,2) NOT NULL COMMENT '变更金额', `remark` varchar(255) DEFAULT NULL COMMENT '备注', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间', `create_by` bigint DEFAULT NULL COMMENT '操作人ID', PRIMARY KEY (`id`), KEY `idx_card_id` (`card_id`), KEY `idx_create_time` (`create_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='充电卡余额变更记录'; USE charging_pile_other; -- 卡片管理操作按钮权限;父菜单按现有卡片管理菜单定位。 INSERT INTO `sys_menu` (`menu_name`, `parent_id`, `order_num`, `path`, `component`, `query`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) SELECT permissions.menu_name, parent.menu_id, permissions.order_num, '#', '', '', 1, 0, 'F', '0', '0', permissions.perms, '#', 'admin', NOW(), '', NULL, '' FROM `sys_menu` parent JOIN ( SELECT '卡片新增' AS menu_name, 1 AS order_num, '/chargingCard/add' AS perms UNION ALL SELECT '卡片编辑', 2, '/chargingCard/edit' UNION ALL SELECT '卡片删除', 3, '/chargingCard/delete' UNION ALL SELECT '卡片启停', 4, '/chargingCard/status' UNION ALL SELECT '余额更新', 5, '/chargingCard/balance' UNION ALL SELECT '卡片导入', 6, '/chargingCard/import' UNION ALL SELECT '卡片导出', 7, '/chargingCard/export' ) permissions WHERE parent.perms = '/chargingCard/list' AND NOT EXISTS ( SELECT 1 FROM `sys_menu` existing WHERE existing.perms = permissions.perms ); -- 默认仅授予管理员角色;其他角色由后台按职责单独授权。 INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) SELECT 1, child.menu_id FROM `sys_menu` child WHERE child.parent_id = ( SELECT parent.menu_id FROM `sys_menu` parent WHERE parent.perms = '/chargingCard/list' LIMIT 1 ) AND child.perms IN ( '/chargingCard/add', '/chargingCard/edit', '/chargingCard/delete', '/chargingCard/status', '/chargingCard/balance', '/chargingCard/import', '/chargingCard/export' ) AND NOT EXISTS ( SELECT 1 FROM `sys_role_menu` existing WHERE existing.role_id = 1 AND existing.menu_id = child.menu_id );