feat: update rsf-design and redis integration
| | |
| | | VITE_API_URL = / |
| | | |
| | | # 代理目标地址(开发环境通过 Vite 代理转发请求到此地址,解决跨域问题) |
| | | VITE_API_PROXY_URL = https://m1.apifoxmock.com/m1/6400575-6097373-default |
| | | VITE_API_PROXY_URL = http://127.0.0.1:8085/ref-server |
| | | |
| | | # Delete console |
| | | VITE_DROP_CONSOLE = false |
| | |
| | | import request from '@/utils/http' |
| | | |
| | | export function buildUserListParams(params = {}) { |
| | | return { |
| | | current: params.current || 1, |
| | | pageSize: params.pageSize || params.size || 20, |
| | | username: params.username, |
| | | nickname: params.nickname, |
| | | phone: params.phone, |
| | | status: params.status, |
| | | deptId: params.deptId |
| | | } |
| | | } |
| | | |
| | | export function buildRoleListParams(params = {}) { |
| | | return { |
| | | current: params.current || 1, |
| | | pageSize: params.pageSize || params.size || 20, |
| | | name: params.name, |
| | | code: params.code, |
| | | memo: params.memo, |
| | | status: params.status |
| | | } |
| | | } |
| | | |
| | | function fetchGetUserList(params) { |
| | | return request.get({ |
| | | url: '/api/user/list', |
| | | params |
| | | }) |
| | | return request.post({ url: '/user/page', params: buildUserListParams(params) }) |
| | | } |
| | | |
| | | function fetchSaveUser(params) { |
| | | return request.post({ url: '/user/save', params }) |
| | | } |
| | | |
| | | function fetchUpdateUser(params) { |
| | | return request.post({ url: '/user/update', params }) |
| | | } |
| | | |
| | | function fetchDeleteUser(id) { |
| | | return request.post({ url: `/user/remove/${id}` }) |
| | | } |
| | | |
| | | function fetchResetUserPassword(params) { |
| | | return request.post({ url: '/auth/reset/password', params }) |
| | | } |
| | | |
| | | function fetchUpdateUserStatus(params) { |
| | | return request.post({ url: '/user/update', params }) |
| | | } |
| | | |
| | | function fetchGetUserDetail(id) { |
| | | return request.get({ url: `/user/${id}` }) |
| | | } |
| | | |
| | | function fetchGetRoleList(params) { |
| | | return request.get({ |
| | | url: '/api/role/list', |
| | | return request.post({ url: '/role/page', params: buildRoleListParams(params) }) |
| | | } |
| | | |
| | | function fetchSaveRole(params) { |
| | | return request.post({ url: '/role/save', params }) |
| | | } |
| | | |
| | | function fetchUpdateRole(params) { |
| | | return request.post({ url: '/role/update', params }) |
| | | } |
| | | |
| | | function fetchDeleteRole(id) { |
| | | return request.post({ url: `/role/remove/${id}` }) |
| | | } |
| | | |
| | | function fetchGetRoleOptions(params) { |
| | | return request.post({ url: '/role/list', params }) |
| | | } |
| | | |
| | | function fetchGetDeptTree(params) { |
| | | return request.post({ url: '/dept/tree', params }) |
| | | } |
| | | |
| | | function fetchGetMenuTree(params) { |
| | | return request.post({ url: '/menu/tree', params }) |
| | | } |
| | | |
| | | function fetchGetRoleScopeList(scopeType, roleId) { |
| | | const urlMap = { |
| | | menu: '/role/scope/list', |
| | | pda: '/rolePda/scope/list', |
| | | matnr: '/roleMatnr/scope/list', |
| | | warehouse: '/roleWarehouse/scope/list' |
| | | } |
| | | return request.get({ url: urlMap[scopeType], params: { roleId } }) |
| | | } |
| | | |
| | | function fetchUpdateRoleScope(scopeType, params) { |
| | | const urlMap = { |
| | | menu: '/role/scope/update', |
| | | pda: '/rolePda/scope/update', |
| | | matnr: '/roleMatnr/scope/update', |
| | | warehouse: '/roleWarehouse/scope/update' |
| | | } |
| | | return request.post({ url: urlMap[scopeType], params }) |
| | | } |
| | | |
| | | function fetchGetUserLoginList(params) { |
| | | return request.post({ |
| | | url: '/userLogin/page', |
| | | params |
| | | }) |
| | | } |
| | | function fetchGetMenuList() { |
| | | return request.get({ |
| | | url: '/api/v3/system/menus/simple' |
| | | }) |
| | | |
| | | function fetchGetMenuList(params) { |
| | | return fetchGetMenuTree(params) |
| | | } |
| | | export { fetchGetMenuList, fetchGetRoleList, fetchGetUserList } |
| | | |
| | | export { |
| | | fetchGetUserList, |
| | | fetchSaveUser, |
| | | fetchUpdateUser, |
| | | fetchDeleteUser, |
| | | fetchResetUserPassword, |
| | | fetchUpdateUserStatus, |
| | | fetchGetUserDetail, |
| | | fetchGetRoleList, |
| | | fetchSaveRole, |
| | | fetchUpdateRole, |
| | | fetchDeleteRole, |
| | | fetchGetRoleOptions, |
| | | fetchGetDeptTree, |
| | | fetchGetMenuTree, |
| | | fetchGetRoleScopeList, |
| | | fetchUpdateRoleScope, |
| | | fetchGetUserLoginList, |
| | | fetchGetMenuList |
| | | } |
| | |
| | | "menu": "Menu Manage" |
| | | } |
| | | }, |
| | | "menu": { |
| | | "basStationArea": "BasStationArea", |
| | | "dashboard": "Dashboard", |
| | | "settings": "Settings", |
| | | "basicInfo": "BasicInfo", |
| | | "system": "System", |
| | | "user": "User", |
| | | "role": "Role", |
| | | "menu": "Menu", |
| | | "host": "Host", |
| | | "department": "Department", |
| | | "token": "Token", |
| | | "operation": "Operation", |
| | | "config": "Config", |
| | | "aiParam": "AI Params", |
| | | "aiPrompt": "Prompts", |
| | | "aiMcpMount": "MCP Mounts", |
| | | "aiCallLog": "AI Observe", |
| | | "tenant": "Tenant", |
| | | "userLogin": "Token", |
| | | "customer": "Customer", |
| | | "shipper": "shipper", |
| | | "matnr": "Matnr", |
| | | "matnrGroup": "MatnrGroup", |
| | | "warehouse": "Warehouse", |
| | | "warehouseAreas": "WarehouseAreas", |
| | | "loc": "Loc", |
| | | "locItem": "LocItem", |
| | | "locType": "LocType", |
| | | "locArea": "locArea", |
| | | "locAreaMat": "Logic Areas", |
| | | "locAreaMatRela": "LocAreaMatRela", |
| | | "container": "Container", |
| | | "contract": "Contract", |
| | | "qlyInspect": "QlyInspect", |
| | | "qlyIsptItem": "qlyIsptItem", |
| | | "dictType": "DictType", |
| | | "dictData": "DictData", |
| | | "companys": "Companys", |
| | | "serialRuleItem": "SerialRuleItem", |
| | | "serialRule": "SerialRule", |
| | | "asnOrder": "AsnOrder", |
| | | "asnOrderItem": "AsnOrderItem", |
| | | "asnOrderLog": "asnOrderLog", |
| | | "asnOrderItemLog": "asnOrderItemLog", |
| | | "purchase": "Purchase", |
| | | "purchaseItem": "PurchaseItem", |
| | | "whMat": "Warehouse Mat", |
| | | "fields": "Extend Fields", |
| | | "fieldsItem": "Extend Fields Items", |
| | | "warehouseAreasItem": "Temp Warehouse Areas Stock", |
| | | "deviceSite": "deviceSite", |
| | | "waitPakin": "WaitPakin", |
| | | "waitPakinItem": "WaitPakinItem", |
| | | "task": "Task", |
| | | "taskItem": "TaskItem", |
| | | "taskLog": "TaskLog", |
| | | "taskItemLog": "TaskItemLog", |
| | | "stock": "Stock Manage", |
| | | "stockItem": "Stock Item", |
| | | "locPreview": "LocItem", |
| | | "histories": "Histories", |
| | | "wareWork": "Warehouse Working", |
| | | "statistics": "Stock Statistics", |
| | | "stockManage": "Stock Manage", |
| | | "logs": "Logs", |
| | | "permissions": "Permissions", |
| | | "delivery": "Delivery", |
| | | "outStock": "Out Stock", |
| | | "outStockItem": "Out Stock Item", |
| | | "inStockPoces": "In Stock Pocess", |
| | | "outStockPoces": "Out Stock Pocess", |
| | | "warehouseStock": "Instant Inventory", |
| | | "deviceBind": "Device Bind", |
| | | "tasks": "Tasks", |
| | | "wave": "Wave Manage", |
| | | "basStation": "BasStation", |
| | | "basContainer": "BasContainer", |
| | | "outBound": "Out Bound", |
| | | "checkOutBound": "Check Out Bound", |
| | | "stockTransfer": "Stock Transfer", |
| | | "waveRule": "Wave Rules", |
| | | "checkOrder": "Check Order", |
| | | "checkDiff": "Check Diff", |
| | | "transfer": "Transfer", |
| | | "transferItem": "Transfer Item", |
| | | "locRevise": "Loc Revise", |
| | | "statisticReport": "Statistical Report", |
| | | "locDeadReport": "Locs Dead Report", |
| | | "stockStatistic": "Stock Statistic", |
| | | "outStatistic": "Out Statistic", |
| | | "inStatistic": "In Statistic", |
| | | "inStatisticItem": "In Statistic Item", |
| | | "outStatisticItem": "Out Statistic Item", |
| | | "statisticCount": "Statistic Count", |
| | | "preparation": "Preparation", |
| | | "check": "Check", |
| | | "abnormal": "Abnormal", |
| | | "platform": "Platform", |
| | | "freeze": "Freeze", |
| | | "transferPoces": "Transfer Process", |
| | | "menuPda": "MenuPda", |
| | | "taskPathTemplate": "TaskPathTemplate", |
| | | "taskPathTemplateNode": "TaskPathTemplateNode", |
| | | "subsystemFlowTemplate": "SubsystemFlowTemplate", |
| | | "flowStepTemplate": "FlowStepTemplate", |
| | | "taskPathTemplateMerge": "TaskPathTemplateMerge", |
| | | "missionFlowStepInstance": "Mission Flow Steps" |
| | | }, |
| | | "table": { |
| | | "form": { |
| | | "reset": "Reset", |
| | |
| | | "menu": "菜单管理" |
| | | } |
| | | }, |
| | | "menu": { |
| | | "basStationArea": "站点区域", |
| | | "dashboard": "控制台", |
| | | "settings": "个人设置", |
| | | "basicInfo": "基础信息", |
| | | "system": "系统设置", |
| | | "user": "用户管理", |
| | | "role": "角色管理", |
| | | "menu": "菜单管理", |
| | | "host": "机构管理", |
| | | "department": "部门管理", |
| | | "token": "登录日志", |
| | | "operation": "操作日志", |
| | | "config": "配置参数", |
| | | "aiParam": "AI 参数", |
| | | "aiPrompt": "Prompt 管理", |
| | | "aiMcpMount": "MCP 挂载", |
| | | "aiCallLog": "AI 观测", |
| | | "tenant": "租户管理", |
| | | "userLogin": "登录日志", |
| | | "customer": "客户表", |
| | | "shipper": "货主信息", |
| | | "matnr": "物料", |
| | | "matnrGroup": "物料分组", |
| | | "warehouse": "仓库", |
| | | "warehouseAreas": "库区", |
| | | "loc": "库位", |
| | | "locItem": "库存明细", |
| | | "locType": "库位类型(废)", |
| | | "locArea": "逻辑分区(废)", |
| | | "locAreaMat": "逻辑分区", |
| | | "locAreaMatRela": "库区物料关系", |
| | | "container": "容器管理(废)", |
| | | "contract": "合同信息(废)", |
| | | "qlyInspect": "质检信息", |
| | | "qlyIsptItem": "质检信息明细", |
| | | "dictType": "数据字典", |
| | | "dictData": "字典数据集", |
| | | "companys": "往来企业", |
| | | "serialRuleItem": "编码规则子表", |
| | | "serialRule": "编码规则", |
| | | "asnOrder": "入库通知单", |
| | | "asnOrderItem": "收货明细", |
| | | "asnOrderLog": "历史通知单", |
| | | "asnOrderItemLog": "收货历史明细", |
| | | "purchase": "PO单", |
| | | "purchaseItem": "PO单明细", |
| | | "whMat": "库区物料关系", |
| | | "fields": "扩展字段", |
| | | "fieldsItem": "扩展字段明细", |
| | | "warehouseAreasItem": "收货库存", |
| | | "deviceSite": "路径管理", |
| | | "waitPakin": "组托档", |
| | | "waitPakinItem": "组托档明细", |
| | | "waitPakinLog": "组托历史档", |
| | | "waitPakinItemLog": "组托历史档明细", |
| | | "task": "任务管理", |
| | | "taskItem": "任务档明细", |
| | | "taskLog": "任务历史档", |
| | | "taskItemLog": "任务明细历史档", |
| | | "stock": "入出库历史", |
| | | "stockItem": "单据明细", |
| | | "locPreview": "库位明细", |
| | | "histories": "历史档", |
| | | "wareWork": "仓库作业", |
| | | "statistics": "库存查询", |
| | | "stockManage": "库存管理", |
| | | "logs": "日志", |
| | | "permissions": "权限管理", |
| | | "delivery": "DO单", |
| | | "outStock": "出库通知单", |
| | | "outStockItem": "出库单明细", |
| | | "inStockPoces": "入库管理", |
| | | "outStockPoces": "出库管理", |
| | | "warehouseStock": "即时库存", |
| | | "deviceBind": "设备绑定", |
| | | "tasks": "任务管理", |
| | | "wave": "波次管理", |
| | | "basStation": "站点管理", |
| | | "basContainer": "容器规则", |
| | | "outBound": "出库作业", |
| | | "checkOutBound": "盘点出库", |
| | | "stockTransfer": "库位转移", |
| | | "waveRule": "波次策略", |
| | | "checkOrder": "盘点单", |
| | | "checkDiff": "盘点差异单", |
| | | "transfer": "调拔单", |
| | | "transferItem": "调拔单明细", |
| | | "locRevise": "库存调整", |
| | | "statisticReport": "报表管理", |
| | | "locDeadReport": "库存停滞报表", |
| | | "stockStatistic": "日入库汇总查询", |
| | | "outStatistic": "日出库汇总查询", |
| | | "inStatistic": "日入库汇总查询", |
| | | "inStatisticItem": "日入库明细查询", |
| | | "outStatisticItem": "日出库明细查询", |
| | | "statisticCount": "日出入库汇总统计", |
| | | "preparation": "备料单", |
| | | "check": "盘点管理", |
| | | "abnormal": "异常管理", |
| | | "platform": "平台管理", |
| | | "freeze": "库存冻结", |
| | | "transferPoces": "调拨管理", |
| | | "menuPda": "PDA菜单", |
| | | "taskPathTemplate": "任务路径模板", |
| | | "taskPathTemplateNode": "任务路径模板节点", |
| | | "subsystemFlowTemplate": "子系统流程模板", |
| | | "flowStepTemplate": "流程步骤模板", |
| | | "taskPathTemplateMerge": "任务路径模板合并", |
| | | "missionFlowStepInstance": "任务流程步骤" |
| | | }, |
| | | "table": { |
| | | "form": { |
| | | "reset": "重置", |
| | |
| | | } |
| | | const formatMenuTitle = (title) => { |
| | | if (title) { |
| | | if (title.startsWith('menus.')) { |
| | | if (title.startsWith('menus.') || title.startsWith('menu.')) { |
| | | if (i18n.global.te(title)) { |
| | | return $t(title) |
| | | } else { |
| | | const fallbackTitle = |
| | | title.startsWith('menus.') && title.split('.').pop() |
| | | ? `menu.${title.split('.').pop()}` |
| | | : '' |
| | | if (fallbackTitle && i18n.global.te(fallbackTitle)) { |
| | | return $t(fallbackTitle) |
| | | } |
| | | return title.split('.').pop() || title |
| | | } |
| | | } |
| | |
| | | import MenuDialog from './modules/menu-dialog.vue' |
| | | |
| | | import { formatMenuTitle } from '@/utils/router' |
| | | import ArtSvgIcon from '@/components/core/base/art-svg-icon/index.vue' |
| | | import ArtButtonTable from '@/components/core/forms/art-button-table/index.vue' |
| | | import { useTableColumns } from '@/hooks/core/useTableColumns' |
| | | import { fetchGetMenuList } from '@/api/system-manage' |
| | |
| | | if (row.meta?.link) return '外链' |
| | | return '未知' |
| | | } |
| | | const getMenuDisplayTitle = (row) => { |
| | | const titleKey = row.meta?.title || row.name || '' |
| | | const normalizedTitleKey = |
| | | titleKey && !String(titleKey).includes('.') ? `menu.${titleKey}` : titleKey |
| | | return formatMenuTitle(normalizedTitleKey) |
| | | } |
| | | const getMenuDisplayIcon = (row) => row.meta?.icon || row.icon || '' |
| | | const { columnChecks, columns } = useTableColumns(() => [ |
| | | { |
| | | prop: 'meta.icon', |
| | | label: '图标预览', |
| | | width: 96, |
| | | align: 'center', |
| | | formatter: (row) => { |
| | | const icon = getMenuDisplayIcon(row) |
| | | |
| | | if (!icon) return h('span', { class: 'text-g-400' }, '-') |
| | | |
| | | return h('div', { class: 'flex items-center justify-center' }, [ |
| | | h(ArtSvgIcon, { icon, class: 'text-base text-g-700' }) |
| | | ]) |
| | | } |
| | | }, |
| | | { |
| | | prop: 'meta.title', |
| | | label: '菜单名称', |
| | | minWidth: 120, |
| | | formatter: (row) => formatMenuTitle(row.meta?.title) |
| | | formatter: (row) => getMenuDisplayTitle(row) |
| | | }, |
| | | { |
| | | prop: 'type', |
| | |
| | | }) |
| | | } |
| | | const searchMenu = (items) => { |
| | | const results = [] |
| | | const results = [] |
| | | for (const item of items) { |
| | | const searchName = appliedFilters.name?.toLowerCase().trim() || '' |
| | | const searchRoute = appliedFilters.route?.toLowerCase().trim() || '' |
| | | const menuTitle = formatMenuTitle(item.meta?.title || '').toLowerCase() |
| | | const menuTitle = getMenuDisplayTitle(item).toLowerCase() |
| | | const menuPath = (item.path || '').toLowerCase() |
| | | const nameMatch = !searchName || menuTitle.includes(searchName) |
| | | const routeMatch = !searchRoute || menuPath.includes(searchRoute) |
| New file |
| | |
| | | import assert from 'node:assert/strict' |
| | | import test from 'node:test' |
| | | import { buildUserListParams, buildRoleListParams } from '../src/api/system-manage.js' |
| | | |
| | | test('buildUserListParams matches the rsf-admin paging contract', () => { |
| | | assert.deepEqual( |
| | | buildUserListParams({ |
| | | current: 2, |
| | | pageSize: 20, |
| | | username: 'root', |
| | | deptId: 3 |
| | | }), |
| | | { |
| | | current: 2, |
| | | pageSize: 20, |
| | | username: 'root', |
| | | deptId: 3 |
| | | } |
| | | ) |
| | | }) |
| New file |
| | |
| | | import assert from 'node:assert/strict' |
| | | import fs from 'node:fs' |
| | | import path from 'node:path' |
| | | import test from 'node:test' |
| | | |
| | | const projectRoot = path.resolve(import.meta.dirname, '..') |
| | | const zhLocalePath = path.join(projectRoot, 'src', 'locales', 'langs', 'zh.json') |
| | | const enLocalePath = path.join(projectRoot, 'src', 'locales', 'langs', 'en.json') |
| | | const routerUtilsPath = path.join(projectRoot, 'src', 'utils', 'router.js') |
| | | const menuPagePath = path.join(projectRoot, 'src', 'views', 'system', 'menu', 'index.vue') |
| | | |
| | | test('current-system menu keys are available in rsf-design locales', () => { |
| | | const zhMessages = JSON.parse(fs.readFileSync(zhLocalePath, 'utf8')) |
| | | const enMessages = JSON.parse(fs.readFileSync(enLocalePath, 'utf8')) |
| | | |
| | | assert.equal(zhMessages.menu?.system, '系统设置') |
| | | assert.equal(zhMessages.menu?.basicInfo, '基础信息') |
| | | assert.equal(zhMessages.menu?.aiParam, 'AI 参数') |
| | | |
| | | assert.equal(enMessages.menu?.system, 'System') |
| | | assert.equal(enMessages.menu?.basicInfo, 'BasicInfo') |
| | | assert.equal(enMessages.menu?.aiParam, 'AI Params') |
| | | }) |
| | | |
| | | test('formatMenuTitle recognizes current-system menu translation keys', () => { |
| | | const routerSource = fs.readFileSync(routerUtilsPath, 'utf8') |
| | | |
| | | assert.match(routerSource, /startsWith\('menu\.'\)|startsWith\("menu\."\)/) |
| | | }) |
| | | |
| | | test('menu management table shows icon preview and translated names', () => { |
| | | const menuPageSource = fs.readFileSync(menuPagePath, 'utf8') |
| | | |
| | | assert.match(menuPageSource, /label:\s*'图标预览'/) |
| | | assert.match(menuPageSource, /ArtSvgIcon/) |
| | | assert.match(menuPageSource, /row\.meta\?\.title\s*\|\|\s*row\.name|item\.meta\?\.title\s*\|\|\s*item\.name/) |
| | | }) |
| New file |
| | |
| | | import assert from 'node:assert/strict' |
| | | import test from 'node:test' |
| | | import { |
| | | buildScopeTreeNodes, |
| | | buildScopeSavePayload, |
| | | SCOPE_TYPES |
| | | } from '../src/views/system/role/roleScope.config.js' |
| | | |
| | | test('menu scope nodes preserve backend ids for save', () => { |
| | | const tree = buildScopeTreeNodes(SCOPE_TYPES.menu, [{ id: 1, label: '系统管理' }]) |
| | | assert.equal(tree[0].id, 1) |
| | | }) |
| | | |
| | | test('scope save payload is delegated per scope type', () => { |
| | | const payload = buildScopeSavePayload(SCOPE_TYPES.menu, { |
| | | roleId: 9, |
| | | selectedIds: [1, 2], |
| | | authType: 0 |
| | | }) |
| | | assert.equal(payload.id, 9) |
| | | }) |
| New file |
| | |
| | | import assert from 'node:assert/strict' |
| | | import test from 'node:test' |
| | | import { buildUserDialogModel } from '../src/views/system/user/userPage.helpers.js' |
| | | |
| | | test('buildUserDialogModel maps rsf-admin edit data into the dialog model', () => { |
| | | assert.equal( |
| | | buildUserDialogModel({ username: 'root', nickname: '管理员', deptId: 1, roles: [{ id: 3 }] }).username, |
| | | 'root' |
| | | ) |
| | | }) |
| | |
| | | |
| | | import java.util.Date; |
| | | import java.util.Set; |
| | | import java.util.function.Consumer; |
| | | import java.util.function.Function; |
| | | |
| | | /** |
| | | * redis tools |
| | |
| | | return null; |
| | | } |
| | | |
| | | private <T> T withJedis(Function<Jedis, T> action) { |
| | | Jedis jedis = this.getJedis(); |
| | | if (jedis == null) { |
| | | return null; |
| | | } |
| | | try (jedis) { |
| | | return action.apply(jedis); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private void withJedisVoid(Consumer<Jedis> action) { |
| | | Jedis jedis = this.getJedis(); |
| | | if (jedis == null) { |
| | | return; |
| | | } |
| | | try (jedis) { |
| | | action.accept(jedis); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | } |
| | | |
| | | // key - object ---------------------------------------------------------------------------------------------------------- |
| | | |
| | | public String set(String flag, String key, Object value) { |
| | |
| | | this.delete(flag, key); |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return jedis.set((flag + LINK + key).getBytes(), Serialize.serialize(value)); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | return withJedis(jedis -> jedis.set((flag + LINK + key).getBytes(), Serialize.serialize(value))); |
| | | } |
| | | |
| | | public String set(String flag, String key, Object value, Integer seconds) { |
| | |
| | | this.delete(flag, key); |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return jedis.setex((flag + LINK + key).getBytes(), seconds, Serialize.serialize(value)); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | return withJedis(jedis -> jedis.setex((flag + LINK + key).getBytes(), seconds, Serialize.serialize(value))); |
| | | } |
| | | |
| | | public <T> T get(String flag, String key) { |
| | | if(!this.initialize) { |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return withJedis(jedis -> { |
| | | byte[] bytes = jedis.get((flag + LINK + key).getBytes()); |
| | | if(bytes == null || bytes.length == 0 ) { |
| | | return null; |
| | | } |
| | | return (T) Serialize.unSerialize(bytes); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | }); |
| | | } |
| | | |
| | | public Long delete(String flag, String key) { |
| | | if(!this.initialize) { |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return jedis.del((flag + LINK + key).getBytes()); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | return withJedis(jedis -> jedis.del((flag + LINK + key).getBytes())); |
| | | } |
| | | |
| | | public Long clear(String flag) { |
| | | if(!this.initialize) { |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | this.setValue(flag, "CLEARING", "true"); |
| | | try{ |
| | | return withJedis(jedis -> { |
| | | Object returnValue = jedis.eval("local keys = redis.call('keys', ARGV[1]) for i=1,#keys,1000 do redis.call('del', unpack(keys, i, math.min(i+4999, #keys))) end return #keys",0,flag + LINK + "*"); |
| | | return Long.parseLong(String.valueOf(returnValue)); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | }); |
| | | } |
| | | |
| | | // 为已存在的key设置过期时间 - 秒 |
| | |
| | | if(!this.initialize) { |
| | | return; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | withJedisVoid(jedis -> { |
| | | jedis.expire((flag + LINK + key).getBytes(), seconds); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // 为已存在的key设置过期时间 - 具体到时间戳 (秒) |
| | |
| | | if(!this.initialize) { |
| | | return; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | withJedisVoid(jedis -> { |
| | | jedis.expireAt((flag + LINK + key).getBytes(), toTime.getTime()/1000); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // 获取过期剩余时间(秒) ttl == -1 没有设置过期时间; ttl == -2 key不存在 |
| | |
| | | if(!this.initialize) { |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return jedis.ttl((flag + LINK + key).getBytes()); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | return withJedis(jedis -> jedis.ttl((flag + LINK + key).getBytes())); |
| | | } |
| | | |
| | | |
| | |
| | | if(!this.initialize) { |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return jedis.set(flag + LINK + key, value); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | return withJedis(jedis -> jedis.set(flag + LINK + key, value)); |
| | | } |
| | | |
| | | public String setValue(String flag, String key, String value, Integer seconds) { |
| | | if(!this.initialize) { |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return jedis.setex(flag + LINK + key, seconds , value); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | return withJedis(jedis -> jedis.setex(flag + LINK + key, seconds , value)); |
| | | } |
| | | |
| | | public String getValue(String flag, String key) { |
| | | if(!this.initialize) { |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return jedis.get(flag + LINK + key); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | return withJedis(jedis -> jedis.get(flag + LINK + key)); |
| | | } |
| | | |
| | | public Long deleteValue(String flag, String... key) { |
| | |
| | | return null; |
| | | } |
| | | |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return withJedis(jedis -> { |
| | | String[] keys = new String[key.length]; |
| | | for(int i=0;i<key.length;i++){ |
| | | keys[i] = flag + LINK + key[i]; |
| | | } |
| | | return jedis.del(keys); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | }); |
| | | } |
| | | |
| | | public Long clearValue(String flag) { |
| | |
| | | } |
| | | |
| | | this.setValue(flag, "CLEARING", "true"); |
| | | Jedis jedis = this.getJedis(); |
| | | |
| | | try{ |
| | | return withJedis(jedis -> { |
| | | Object returnValue = jedis.eval("return redis.call('del', unpack(redis.call('keys', ARGV[1])))",0,flag + LINK + "*"); |
| | | return Long.parseLong(String.valueOf(returnValue)); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | }); |
| | | } |
| | | |
| | | public void setValueExpire(String flag, String key,int seconds){ |
| | | if(!this.initialize) { |
| | | return; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | withJedisVoid(jedis -> { |
| | | jedis.expire((flag + LINK + key).getBytes(), seconds); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | public void setValueExpireAt(String flag, String key,Date atTime){ |
| | | if(!this.initialize) { |
| | | return; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | withJedisVoid(jedis -> { |
| | | jedis.expireAt((flag + LINK + key).getBytes(), atTime.getTime()/1000); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | |
| | |
| | | deleteMap(name,key); |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try { |
| | | return jedis.hset(name.getBytes(), key.getBytes(), Serialize.serialize(value)); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | return withJedis(jedis -> jedis.hset(name.getBytes(), key.getBytes(), Serialize.serialize(value))); |
| | | } |
| | | |
| | | public <T> T getMap(String name, String key) { |
| | | if(!this.initialize) { |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return withJedis(jedis -> { |
| | | byte[] bytes = jedis.hget(name.getBytes(), key.getBytes()); |
| | | if (bytes == null || bytes.length == 0) { |
| | | return null; |
| | | } |
| | | return (T) Serialize.unSerialize(bytes); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | }); |
| | | } |
| | | |
| | | public Set<String> getMapKeys(String name) { |
| | | if(!this.initialize) { |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return jedis.hkeys(name); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | return withJedis(jedis -> jedis.hkeys(name)); |
| | | } |
| | | |
| | | public Long deleteMap(String name, String... key) { |
| | | if(!this.initialize) { |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return withJedis(jedis -> { |
| | | String[] keys = new String[key.length]; |
| | | System.arraycopy(key, 0, keys, 0, key.length); |
| | | return jedis.hdel(name, keys); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | }); |
| | | } |
| | | |
| | | public Long clearMap(String name) { |
| | | if(!this.initialize) { |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return jedis.del(name); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | return withJedis(jedis -> jedis.del(name)); |
| | | } |
| | | |
| | | public void setMapExpire(String name,int seconds){ |
| | | if(!this.initialize) { |
| | | return; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | withJedisVoid(jedis -> { |
| | | jedis.expire(name.getBytes(), seconds); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | public void setMapExpireAt(String name,Date atTime){ |
| | | if(!this.initialize) { |
| | | return; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | withJedisVoid(jedis -> { |
| | | jedis.expireAt(name.getBytes(), atTime.getTime()/1000); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | |
| | |
| | | if(value == null){ |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return jedis.rpush(name.getBytes(), Serialize.serialize(value)); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | return withJedis(jedis -> jedis.rpush(name.getBytes(), Serialize.serialize(value))); |
| | | } |
| | | |
| | | // 获取列表头部元素 && 删除 |
| | |
| | | if(!this.initialize){ |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return withJedis(jedis -> { |
| | | byte[] bytes = jedis.lpop(name.getBytes()); |
| | | if(bytes == null || bytes.length == 0) { |
| | | return null; |
| | | } |
| | | return (T) Serialize.unSerialize(bytes); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | }); |
| | | } |
| | | |
| | | // 删除 |
| | |
| | | if(!this.initialize) { |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return jedis.del(name); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | return withJedis(jedis -> jedis.del(name)); |
| | | } |
| | | |
| | | public void setListExpire(String name, int seconds){ |
| | | if(!this.initialize) { |
| | | return; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | withJedisVoid(jedis -> { |
| | | jedis.expire(name.getBytes(), seconds); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | public void setListExpireAt(String name, Date atTime){ |
| | | if(!this.initialize) { |
| | | return; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | withJedisVoid(jedis -> { |
| | | jedis.expireAt(name.getBytes(), atTime.getTime()/1000); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // count ---------------------------------------------------------------------------------------------------------- |
| | |
| | | if(!this.initialize) { |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return jedis.incr("COUNT." + key); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | return withJedis(jedis -> jedis.incr("COUNT." + key)); |
| | | } |
| | | |
| | | public Long decr(String key) { |
| | | if(!this.initialize) { |
| | | return null; |
| | | } |
| | | Jedis jedis = this.getJedis(); |
| | | try{ |
| | | return jedis.decr("COUNT." + key); |
| | | } catch (Exception e) { |
| | | log.error(this.getClass().getSimpleName(), e); |
| | | } |
| | | return null; |
| | | return withJedis(jedis -> jedis.decr("COUNT." + key)); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.vincent.rsf.server.common.service; |
| | | |
| | | import com.vincent.rsf.common.utils.Serialize; |
| | | import org.junit.jupiter.api.BeforeEach; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.junit.jupiter.api.extension.ExtendWith; |
| | | import org.mockito.Mock; |
| | | import org.mockito.junit.jupiter.MockitoExtension; |
| | | import redis.clients.jedis.Jedis; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | import static org.mockito.Mockito.verify; |
| | | import static org.mockito.Mockito.when; |
| | | |
| | | @ExtendWith(MockitoExtension.class) |
| | | class RedisServiceTest { |
| | | |
| | | @Mock |
| | | private Jedis jedis; |
| | | |
| | | private RedisService redisService; |
| | | |
| | | @BeforeEach |
| | | void setUp() { |
| | | redisService = new RedisService() { |
| | | @Override |
| | | public Jedis getJedis() { |
| | | return jedis; |
| | | } |
| | | }; |
| | | redisService.initialize = true; |
| | | } |
| | | |
| | | @Test |
| | | void getClosesBorrowedJedisAfterReadingValue() { |
| | | when(jedis.get("MENU_TREE.FULL_TREE".getBytes())).thenReturn(Serialize.serialize("cached")); |
| | | |
| | | String value = redisService.get("MENU_TREE", "FULL_TREE"); |
| | | |
| | | assertEquals("cached", value); |
| | | verify(jedis).close(); |
| | | } |
| | | |
| | | @Test |
| | | void deleteClosesBorrowedJedisAfterDeletingKey() { |
| | | when(jedis.del("MENU_TREE.FULL_TREE".getBytes())).thenReturn(1L); |
| | | |
| | | Long deleted = redisService.delete("MENU_TREE", "FULL_TREE"); |
| | | |
| | | assertEquals(1L, deleted); |
| | | verify(jedis).close(); |
| | | } |
| | | } |