From 8afdb0d09e35158ec513a1ffdc6ab2957a50e211 Mon Sep 17 00:00:00 2001 From: wuxichen <17301714657@163.com> Date: Thu, 18 Sep 2025 17:47:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B6=88=E6=81=AF=E4=B8=AD=E5=BF=83-?= =?UTF-8?q?=E7=AB=99=E5=86=85=E4=BF=A1=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.tsx | 90 ++++---- src/components/ModalDescriptions/index.tsx | 2 +- src/constants/index.ts | 2 +- src/pages/system/log/login/config.tsx | 105 ++++++++++ src/pages/system/log/login/index.tsx | 64 +++++- src/pages/system/messages/notice/config.tsx | 105 ++++++++++ src/pages/system/messages/notice/index.tsx | 126 ++++++++++++ .../system/messages/notify/message/config.tsx | 142 +++++++++++++ .../system/messages/notify/message/index.tsx | 70 +++++++ .../messages/notify/template/config.tsx | 193 ++++++++++++++++++ .../system/messages/notify/template/index.tsx | 174 ++++++++++++++++ .../system/messages/sms/channel/config.tsx | 105 ++++++++++ .../system/messages/sms/channel/index.tsx | 67 ++++++ src/pages/system/tenant/list/index.tsx | 59 +++--- src/requestErrorConfig.ts | 99 +++++---- src/services/system/log/login.ts | 27 +++ src/services/system/log/operate.ts | 5 - src/services/system/message/mail/account.tsx | 72 +++++++ src/services/system/message/mail/log.tsx | 41 ++++ src/services/system/message/mail/template.tsx | 81 ++++++++ src/services/system/message/notice.tsx | 74 +++++++ .../system/message/notify/message.tsx | 76 +++++++ .../system/message/notify/template.tsx | 80 ++++++++ src/services/system/message/sms/channel.tsx | 73 +++++++ src/services/system/message/sms/log.tsx | 40 ++++ src/services/system/message/sms/template.tsx | 92 +++++++++ 26 files changed, 1929 insertions(+), 135 deletions(-) create mode 100644 src/pages/system/log/login/config.tsx create mode 100644 src/pages/system/messages/notice/config.tsx create mode 100644 src/pages/system/messages/notice/index.tsx create mode 100644 src/pages/system/messages/notify/message/config.tsx create mode 100644 src/pages/system/messages/notify/message/index.tsx create mode 100644 src/pages/system/messages/notify/template/config.tsx create mode 100644 src/pages/system/messages/notify/template/index.tsx create mode 100644 src/pages/system/messages/sms/channel/config.tsx create mode 100644 src/pages/system/messages/sms/channel/index.tsx create mode 100644 src/services/system/log/login.ts create mode 100644 src/services/system/message/mail/account.tsx create mode 100644 src/services/system/message/mail/log.tsx create mode 100644 src/services/system/message/mail/template.tsx create mode 100644 src/services/system/message/notice.tsx create mode 100644 src/services/system/message/notify/message.tsx create mode 100644 src/services/system/message/notify/template.tsx create mode 100644 src/services/system/message/sms/channel.tsx create mode 100644 src/services/system/message/sms/log.tsx create mode 100644 src/services/system/message/sms/template.tsx diff --git a/src/app.tsx b/src/app.tsx index 2f603a4..e216283 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -1,38 +1,38 @@ +import type { Settings as LayoutSettings } from '@ant-design/pro-components'; +import { SettingDrawer } from '@ant-design/pro-components'; +import type { RequestConfig, RunTimeLayoutConfig } from '@umijs/max'; +import { history, Link, Navigate } from '@umijs/max'; +import { Modal, Spin } from 'antd'; import React, { Children, Component, createContext, JSX, Suspense, -} from "react"; -import { Modal, Spin } from "antd"; -import type { Settings as LayoutSettings } from "@ant-design/pro-components"; -import { SettingDrawer } from "@ant-design/pro-components"; -import type { RequestConfig, RunTimeLayoutConfig } from "@umijs/max"; -import { history, Link, Navigate } from "@umijs/max"; +} from 'react'; import { AvatarDropdown, AvatarName, Footer, Question, SelectLang, -} from "@/components"; -import { getInfo } from "@/services/login"; -import type { UserVO, TokenType, UserInfoVO } from "@/services/login/types"; -import defaultSettings from "../config/defaultSettings"; -import { errorConfig } from "./requestErrorConfig"; -import "@ant-design/v5-patch-for-react-19"; -import { getAccessToken, getRefreshToken, getTenantId } from "@/utils/auth"; -import { CACHE_KEY, useCache } from "./hooks/web/useCache"; -import { MenuVO } from "./services/system/menu"; - +} from '@/components'; +import { getInfo } from '@/services/login'; +import type { TokenType, UserInfoVO, UserVO } from '@/services/login/types'; +import defaultSettings from '../config/defaultSettings'; +import { errorConfig } from './requestErrorConfig'; +import '@ant-design/v5-patch-for-react-19'; +import { getAccessToken, getRefreshToken, getTenantId } from '@/utils/auth'; import { transformBackendMenuToFlatRoutes, transformMenuToRoutes, -} from "@/utils/menuUtils"; -const isDev = process.env.NODE_ENV === "development"; +} from '@/utils/menuUtils'; +import { CACHE_KEY, useCache } from './hooks/web/useCache'; +import { MenuVO } from './services/system/menu'; + +const isDev = process.env.NODE_ENV === 'development'; const isDevOrTest = isDev || process.env.CI; -const loginPath = "/user/login"; +const loginPath = '/user/login'; // 全局存储菜单数据和路由映射 @@ -51,7 +51,7 @@ export async function getInitialState(): Promise<{ try { const token = getAccessToken(); if (!token) { - throw new Error("No token found"); + throw new Error('No token found'); } const data = await getInfo(); wsCache.set(CACHE_KEY.USER, data); @@ -69,8 +69,8 @@ export async function getInitialState(): Promise<{ const { location } = history; if ( - ![loginPath, "/user/register", "/user/register-result"].includes( - location.pathname + ![loginPath, '/user/register', '/user/register-result'].includes( + location.pathname, ) ) { const currentUser = wsCache.get(CACHE_KEY.USER); @@ -125,22 +125,22 @@ export const layout: RunTimeLayoutConfig = ({ }, bgLayoutImgList: [ { - src: "https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/D2LWSqNny4sAAAAAAAAAAAAAFl94AQBr", + src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/D2LWSqNny4sAAAAAAAAAAAAAFl94AQBr', left: 85, bottom: 100, - height: "303px", + height: '303px', }, { - src: "https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/C2TWRpJpiC0AAAAAAAAAAAAAFl94AQBr", + src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/C2TWRpJpiC0AAAAAAAAAAAAAFl94AQBr', bottom: -68, right: -45, - height: "303px", + height: '303px', }, { - src: "https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/F6vSTbj8KpYAAAAAAAAAAAAAFl94AQBr", + src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/F6vSTbj8KpYAAAAAAAAAAAAAFl94AQBr', bottom: 0, left: 0, - width: "331px", + width: '331px', }, ], menuHeaderRender: undefined, @@ -178,14 +178,14 @@ export const layout: RunTimeLayoutConfig = ({ * @doc https://umijs.org/docs/max/request#配置 */ export const request: RequestConfig = { - baseURL: isDev ? "" : "https://proapi.azurewebsites.net", + baseURL: isDev ? '' : 'https://proapi.azurewebsites.net', ...errorConfig, // 添加请求拦截器 requestInterceptors: [ (url, options) => { // 为所有请求添加 API 前缀 - if (url && !url.startsWith(process.env.API_PREFIX || "/admin-api")) { - url = (process.env.API_PREFIX || "/admin-api") + url; + if (url && !url.startsWith(process.env.API_PREFIX || '/admin-api')) { + url = (process.env.API_PREFIX || '/admin-api') + url; } // 获取存储在本地的 token 和 tenantId const token = getAccessToken(); @@ -193,12 +193,12 @@ export const request: RequestConfig = { // 设置统一的请求头 const headers: Record = { ...options.headers, - Accept: "*", - "tenant-id": tenantId, + Accept: '*', + 'tenant-id': tenantId, }; // 如果有token,则添加Authorization头 if (token) { - headers["Authorization"] = `Bearer ${getAccessToken()}`; + headers['Authorization'] = `Bearer ${getAccessToken()}`; } return { url, options: { ...options, headers } }; }, @@ -210,7 +210,7 @@ export const request: RequestConfig = { const appendParams = (key: string, value: any) => { if (Array.isArray(value)) { // 特殊处理 createTime 数组,转换为 createTime[0] 和 createTime[1] 格式 - if (key === "createTime") { + if (key === 'createTime') { value.forEach((val, index) => { searchParams.append(`${key}[${index}]`, val); }); @@ -237,23 +237,23 @@ export function patchClientRoutes({ routes }: { routes: any }) { const { wsCache } = useCache(); console.log(2222); const globalMenus = wsCache.get(CACHE_KEY.ROLE_ROUTERS); - const routerIndex = routes.findIndex((item: any) => item.path === "/"); + const routerIndex = routes.findIndex((item: any) => item.path === '/'); const parentId = routes[routerIndex].id; if (globalMenus) { - routes[routerIndex]["routes"].push(...loopMenuItem(globalMenus, parentId)); + routes[routerIndex]['routes'].push(...loopMenuItem(globalMenus, parentId)); } } const loopMenuItem = (menus: any[], pId: number | string): any[] => { return menus.flatMap((item) => { let Component: React.ComponentType | null = null; - console.log(findFirstLeafRoute(item), "item"); + console.log(findFirstLeafRoute(item), 'item'); if (item.component && item.component.length > 0) { // 防止配置了路由,但本地暂未添加对应的页面,产生的错误 Component = React.lazy(() => { const importComponent = () => import(`@/pages/${item.component}`); - const import404 = () => import("@/pages/404"); + const import404 = () => import('@/pages/404'); return importComponent().catch(import404); }); } @@ -262,7 +262,7 @@ const loopMenuItem = (menus: any[], pId: number | string): any[] => { { path: item.path, name: item.name, - icon: item.icon, + // icon: item.icon, id: item.id, parentId: pId, @@ -281,13 +281,13 @@ const loopMenuItem = (menus: any[], pId: number | string): any[] => { { path: item.path, name: item.name, - icon: item.icon, + // icon: item.icon, id: item.menuID, parentId: pId, - redirect: "/system/tenant/list", + redirect: '/system/tenant/list', element: ( } + fallback={} > {Component && } @@ -299,7 +299,7 @@ const loopMenuItem = (menus: any[], pId: number | string): any[] => { }); }; -const findFirstLeafRoute = (menuItem: any, parent = "/"): string | null => { +const findFirstLeafRoute = (menuItem: any, parent = '/'): string | null => { // 如果没有子菜单,返回当前路径 if (!menuItem.children || menuItem.children.length === 0) { @@ -308,7 +308,7 @@ const findFirstLeafRoute = (menuItem: any, parent = "/"): string | null => { // 递归查找第一个叶子节点 for (const child of menuItem.children) { - const leafRoute = findFirstLeafRoute(child, menuItem.path + "/"); + const leafRoute = findFirstLeafRoute(child, menuItem.path + '/'); if (leafRoute) { return leafRoute; } diff --git a/src/components/ModalDescriptions/index.tsx b/src/components/ModalDescriptions/index.tsx index 33c2c52..6f0a2f4 100644 --- a/src/components/ModalDescriptions/index.tsx +++ b/src/components/ModalDescriptions/index.tsx @@ -49,7 +49,7 @@ const ModalDescriptions = forwardRef((props: Props, ref) => { footer={null} > [] = [ + { + title: '日志编号', + dataIndex: 'id', + tip: '日志编号', + width: 100, + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '操作类型', + dataIndex: 'logType', + hideInSearch: true, // 在搜索表单中隐藏 + tip: '操作类型', // 提示信息 + }, + { + title: '操作模块', + dataIndex: 'type', + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '用户名称', + dataIndex: 'username', + }, + { + title: '登录地址', + dataIndex: 'userIp', + }, + + { + title: '浏览器', + dataIndex: 'userAgent', + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '登录结果', + dataIndex: 'result', + hideInSearch: true, + }, + { + title: '登录日期', + dataIndex: 'createTime', + valueType: 'dateRange', + search: { + transform: (value) => { + return { + [`createTime[0]`]: dayjs(value[0]) + .startOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + [`createTime[1]`]: dayjs(value[1]) + .endOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + }; + }, + }, + render: (_, record: LoginLogVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, +]; + +export const descriptionsColumns = (): ProDescriptionsItemProps< + Record, + 'text' +>[] => [ + { + title: '日志编号', + key: 'id', + dataIndex: 'id', + }, + { + title: '操作类型', + key: 'logType', + dataIndex: 'logType', + }, + { + title: '用户名称', + key: 'username', + dataIndex: 'username', + }, + { + title: '登录地址', + key: 'userIp', + dataIndex: 'userIp', + }, + { + title: '浏览器', + key: 'userAgent', + dataIndex: 'userAgent', + }, + { + title: '登录结果', + key: 'result', + dataIndex: 'result', + }, + { + title: '登录日期', + key: 'createTime', + dataIndex: 'createTime', + }, +]; diff --git a/src/pages/system/log/login/index.tsx b/src/pages/system/log/login/index.tsx index e47f9fb..01d36c4 100644 --- a/src/pages/system/log/login/index.tsx +++ b/src/pages/system/log/login/index.tsx @@ -1,5 +1,67 @@ +import type { ActionType, ProColumns } from '@ant-design/pro-components'; +import React, { useRef } from 'react'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import ModalDescriptions, { + type DescriptionsFormRef, +} from '@/components/ModalDescriptions'; +import { getLoginLogPage, type LoginLogVO } from '@/services/system/log/login'; +import { baseTenantColumns, descriptionsColumns } from './config'; + const SyStemLogLogin = () => { - return <>SyStemLogLogin; + const tableRef = useRef(null); + const descriptionsRef = useRef(null); + const onFetch = async ( + params: LoginLogVO & { + pageSize?: number; + current?: number; + }, + ) => { + const data = await getLoginLogPage({ + ...params, + pageNo: params.current, + pageSize: params.pageSize, + }); + return { + data: data.list, + success: true, + total: data.total, + }; + }; + + const handleDetail = (record: LoginLogVO) => { + descriptionsRef.current?.open(record); + }; + + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 80, + render: (text: React.ReactNode, record: LoginLogVO) => [ + handleDetail(record)}> + 详情 + , + ], + }; + const columns = [...baseTenantColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + headerTitle="登录日志" + showIndex={false} + showSelection={false} + /> + + + ); }; export default SyStemLogLogin; diff --git a/src/pages/system/messages/notice/config.tsx b/src/pages/system/messages/notice/config.tsx new file mode 100644 index 0000000..650c2e0 --- /dev/null +++ b/src/pages/system/messages/notice/config.tsx @@ -0,0 +1,105 @@ +import type { + ProColumns, + ProDescriptionsItemProps, + ProFormColumnsType, +} from '@ant-design/pro-components'; +import dayjs from 'dayjs'; +import type { NoticeVO } from '@/services/system/message/notice'; +export const baseTenantColumns: ProColumns[] = [ + { + title: '公告编号', + dataIndex: 'id', + width: 100, + hideInSearch: true, + }, + { + title: '公告标题', + dataIndex: 'title', + }, + { + title: '公告类型', + dataIndex: 'type', + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '状态', + dataIndex: 'status', + }, + { + title: '创建时间', + dataIndex: 'createTime', + valueType: 'dateRange', + hideInSearch: true, + render: (_, record: NoticeVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, +]; + +export const formColumns = (type: string): ProFormColumnsType[] => [ + { + title: '公告标题', + dataIndex: 'title', + tip: '公告标题', // 提示信息 + formItemProps: { + rules: [ + { + required: true, + message: '请输入用户名', + }, + ], + }, + }, + { + title: '公告内容', + dataIndex: 'content', + formItemProps: { + rules: [ + { + required: true, + message: '请输入公告内容', + }, + ], + }, + }, + { + title: '公告类型', + dataIndex: 'type', + formItemProps: { + rules: [ + { + required: true, + message: '请输入公告类型', + }, + ], + }, + }, + { + title: '状态', + dataIndex: 'status', + valueType: 'select', + fieldProps: { + options: [ + { + label: '正常', + value: 1, + }, + { + label: '禁用', + value: 2, + }, + ], + }, + formItemProps: { + rules: [ + { + required: true, + message: '请选择状态', + }, + ], + }, + }, + { + title: '备注', + dataIndex: 'username', + }, +]; diff --git a/src/pages/system/messages/notice/index.tsx b/src/pages/system/messages/notice/index.tsx new file mode 100644 index 0000000..a251fa5 --- /dev/null +++ b/src/pages/system/messages/notice/index.tsx @@ -0,0 +1,126 @@ +import type { + ActionType, + ProColumns, + ProCoreActionType, +} from '@ant-design/pro-components'; +import { Popconfirm } from 'antd'; +import React, { useCallback, useRef, useState } from 'react'; +import ConfigurableDrawerForm, { + type ConfigurableDrawerFormRef, +} from '@/components/DrawerForm'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import { formStatusType } from '@/constants'; +import { + createNotice, + deleteNotice, + getNoticePage, + type NoticeVO, + pushNotice, + updateNotice, +} from '@/services/system/message/notice'; +import { baseTenantColumns, formColumns } from './config'; + +const SyStemMessageNotice = () => { + const tableRef = useRef(null); + const configurableDrawerRef = useRef(null); + const [type, setType] = useState<'create' | 'update'>('create'); + const [id, setId] = useState(0); + const onFetch = async ( + params: NoticeVO & { + pageSize?: number; + current?: number; + }, + ) => { + const data = await getNoticePage({ + ...params, + pageNo: params.current, + pageSize: params.pageSize, + }); + return { + data: data.list, + success: true, + total: data.total, + }; + }; + + const handleSend = async (record: NoticeVO) => { + await pushNotice(record.id); + tableRef.current?.reload(); + }; + + const handleEdit = (record: NoticeVO) => { + setType('update'); + setId(record.id); + configurableDrawerRef.current?.open(record); + }; + const handleSubmit = useCallback( + async (values: NoticeVO) => { + if (type === 'create') { + await createNotice(values); + } else { + await updateNotice({ + ...values, + id, + }); + } + tableRef.current?.reload(); + return true; + }, + [type, id], + ); + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 120, + render: ( + text: React.ReactNode, + record: NoticeVO, + _: number, + action: ProCoreActionType | undefined, + ) => [ + handleEdit(record)}> + 编辑 + , + { + await deleteNotice(record.id); + action?.reload?.(); + }} + okText="是" + cancelText="否" + > + + 删除 + + , + handleSend(record)}> + 推送 + , + ], + }; + const columns = [...baseTenantColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + headerTitle="登录日志" + showIndex={false} + showSelection={false} + /> + + + ); +}; + +export default SyStemMessageNotice; diff --git a/src/pages/system/messages/notify/message/config.tsx b/src/pages/system/messages/notify/message/config.tsx new file mode 100644 index 0000000..1d07850 --- /dev/null +++ b/src/pages/system/messages/notify/message/config.tsx @@ -0,0 +1,142 @@ +import type { + ProColumns, + ProDescriptionsItemProps, +} from '@ant-design/pro-components'; +import dayjs from 'dayjs'; +import type { NotifyMessageVO } from '@/services/system/message/notify/message'; +export const baseTenantColumns: ProColumns[] = [ + { + title: '编号', + dataIndex: 'id', + width: 100, + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '用户类型', + dataIndex: 'userType', + }, + { + title: '用户编号', + dataIndex: 'userId', + }, + { + title: '模板编码', + dataIndex: 'templateCode', + }, + { + title: '发送人名称', + dataIndex: 'templateNickname', + hideInSearch: true, // 在搜索表单中隐藏 + }, + + { + title: '模版内容', + dataIndex: 'templateContent', + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '模版参数', + dataIndex: 'templateParams', + hideInSearch: true, + }, + { + title: '模版类型', + dataIndex: 'templateType', + valueType: 'select', + }, + { + title: '是否已读', + dataIndex: 'readStatus', + hideInSearch: true, + }, + { + title: '阅读时间', + dataIndex: 'readTime', + hideInSearch: true, + }, + { + title: '创建时间', + dataIndex: 'createTime', + valueType: 'dateRange', + search: { + transform: (value) => { + return { + [`createTime[0]`]: dayjs(value[0]) + .startOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + [`createTime[1]`]: dayjs(value[1]) + .endOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + }; + }, + }, + render: (_, record: NotifyMessageVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, +]; + +export const descriptionsColumns = (): ProDescriptionsItemProps< + Record, + 'text' +>[] => [ + { + title: '编号', + key: 'id', + dataIndex: 'id', + }, + { + title: '用户类型', + key: 'userType', + dataIndex: 'userType', + }, + { + title: '用户编号', + key: 'userId', + dataIndex: 'userId', + }, + { + title: '模版编号', + key: 'templateId', + dataIndex: 'templateId', + }, + { + title: '模板编码', + key: 'templateCode', + dataIndex: 'templateCode', + }, + { + title: '发送人名称', + key: 'templateNickname', + dataIndex: 'templateNickname', + }, + { + title: '模版内容', + key: 'templateContent', + dataIndex: 'templateContent', + }, + { + title: '模版参数', + key: 'createTime', + dataIndex: 'createTime', + }, + { + title: '模版类型', + key: 'templateType', + dataIndex: 'templateType', + }, + { + title: '是否已读', + key: 'readStatus', + dataIndex: 'readStatus', + }, + { + title: '阅读时间', + key: 'readTime', + dataIndex: 'readTime', + }, + { + title: '创建时间', + key: 'createTime', + dataIndex: 'createTime', + }, +]; diff --git a/src/pages/system/messages/notify/message/index.tsx b/src/pages/system/messages/notify/message/index.tsx new file mode 100644 index 0000000..69b7eca --- /dev/null +++ b/src/pages/system/messages/notify/message/index.tsx @@ -0,0 +1,70 @@ +import type { ActionType, ProColumns } from '@ant-design/pro-components'; +import React, { useRef } from 'react'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import ModalDescriptions, { + type DescriptionsFormRef, +} from '@/components/ModalDescriptions'; +import { + getNotifyMessagePage, + type NotifyMessageVO, +} from '@/services/system/message/notify/message'; +import { baseTenantColumns, descriptionsColumns } from './config'; + +const SyStemMessageNotifyMessage = () => { + const tableRef = useRef(null); + const descriptionsRef = useRef(null); + const onFetch = async ( + params: NotifyMessageVO & { + pageSize?: number; + current?: number; + }, + ) => { + const data = await getNotifyMessagePage({ + ...params, + pageNo: params.current, + pageSize: params.pageSize, + }); + return { + data: data.list, + success: true, + total: data.total, + }; + }; + + const handleDetail = (record: NotifyMessageVO) => { + descriptionsRef.current?.open(record); + }; + + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 80, + render: (text: React.ReactNode, record: NotifyMessageVO) => [ + handleDetail(record)}> + 详情 + , + ], + }; + const columns = [...baseTenantColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + headerTitle="登录日志" + showIndex={false} + showSelection={false} + /> + + + ); +}; + +export default SyStemMessageNotifyMessage; diff --git a/src/pages/system/messages/notify/template/config.tsx b/src/pages/system/messages/notify/template/config.tsx new file mode 100644 index 0000000..a0b9d5e --- /dev/null +++ b/src/pages/system/messages/notify/template/config.tsx @@ -0,0 +1,193 @@ +import type { + ProColumns, + ProDescriptionsItemProps, + ProFormColumnsType, +} from '@ant-design/pro-components'; +import dayjs from 'dayjs'; +import type { NotifyTemplateVO } from '@/services/system/message/notify/template'; +export const baseTenantColumns: ProColumns[] = [ + { + title: '模板编码', + dataIndex: 'code', + width: 100, + }, + { + title: '模板名称', + dataIndex: 'name', + }, + { + title: '类型', + dataIndex: 'type', + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '发送人名称', + dataIndex: 'nickname', + hideInSearch: true, + }, + { + title: '模板内容', + dataIndex: 'content', + hideInSearch: true, + }, + { + title: '开启状态', + dataIndex: 'status', + }, + { + title: '备注', + dataIndex: 'remark', + hideInSearch: true, + }, + { + title: '创建时间', + dataIndex: 'createTime', + valueType: 'dateRange', + hideInSearch: true, + render: (_, record: NotifyTemplateVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, +]; + +export const formColumns = (type: string): ProFormColumnsType[] => [ + { + title: '模版编码', + dataIndex: 'code', + tip: '模版编码', // 提示信息 + formItemProps: { + rules: [ + { + required: true, + message: '请输入模版编码', + }, + ], + }, + }, + { + title: '模板名称', + dataIndex: 'name', + formItemProps: { + rules: [ + { + required: true, + message: '请输入模版名称', + }, + ], + }, + }, + { + title: '发件人名称', + dataIndex: 'nickname', + formItemProps: { + rules: [ + { + required: true, + message: '请输入发件人名称', + }, + ], + }, + }, + { + title: '模板内容', + dataIndex: 'content', + formItemProps: { + rules: [ + { + required: true, + message: '请输入模板内容', + }, + ], + }, + }, + { + title: '类型', + dataIndex: 'type', + + formItemProps: { + rules: [ + { + required: true, + message: '请选择类型', + }, + ], + }, + }, + { + title: '开启状态', + dataIndex: 'status', + valueType: 'select', + + fieldProps: { + placeholder: '请选择开启状态', + options: [ + { + label: '正常', + value: 1, + }, + { + label: '禁用', + value: 2, + }, + ], + }, + formItemProps: { + rules: [ + { + required: true, + message: '请选择状态', + }, + ], + }, + }, + { + title: '备注', + dataIndex: 'remark', + fieldProps: { + placeholder: '请输入备注', + }, + }, +]; + +export const formTestColumns = (type: string): ProFormColumnsType[] => [ + { + title: '模板内容', + dataIndex: 'content', + }, + { + title: '用户类型', + dataIndex: 'userType', + valueType: 'radio', + fieldProps: { + options: [ + { + label: '用户', + value: 1, + }, + { + label: '管理员', + value: 2, + }, + ], + }, + formItemProps: { + rules: [ + { + required: true, + message: '请输入模版名称', + }, + ], + }, + }, + { + title: '接收人ID', + dataIndex: 'userId', + formItemProps: { + rules: [ + { + required: true, + message: '请输入接收人ID', + }, + ], + }, + }, +]; diff --git a/src/pages/system/messages/notify/template/index.tsx b/src/pages/system/messages/notify/template/index.tsx new file mode 100644 index 0000000..72638a8 --- /dev/null +++ b/src/pages/system/messages/notify/template/index.tsx @@ -0,0 +1,174 @@ +import { PlusOutlined } from '@ant-design/icons'; +import type { + ActionType, + ProColumns, + ProCoreActionType, +} from '@ant-design/pro-components'; +import { message, Popconfirm } from 'antd'; +import React, { useCallback, useRef, useState } from 'react'; +import ConfigurableDrawerForm, { + type ConfigurableDrawerFormRef, +} from '@/components/DrawerForm'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import type { ToolbarAction } from '@/components/EnhancedProTable/types'; +import { formStatusType } from '@/constants'; +import { + createNotifyTemplate, + deleteNotifyTemplate, + getNotifyTemplatePage, + type NotifyTemplateVO, + sendNotify, + updateNotifyTemplate, +} from '@/services/system/message/notify/template'; +import { baseTenantColumns, formColumns, formTestColumns } from './config'; + +const SyStemMessageNotifyTemplate = () => { + const tableRef = useRef(null); + const configurableDrawerRef = useRef(null); + const [type, setType] = useState<'create' | 'update' | 'test'>('create'); + const testRef = useRef(null); + const [currentItem, setCurrentItem] = useState(); + const onFetch = async ( + params: NotifyTemplateVO & { + pageSize?: number; + current?: number; + }, + ) => { + const data = await getNotifyTemplatePage({ + ...params, + pageNo: params.current, + pageSize: params.pageSize, + }); + return { + data: data.list, + success: true, + total: data.total, + }; + }; + + const handleSend = async (record: NotifyTemplateVO) => { + setType('test'); + setCurrentItem(record); + testRef.current?.open(record); + }; + + const handleEdit = (record: NotifyTemplateVO) => { + setType('update'); + setCurrentItem(record); + + configurableDrawerRef.current?.open(record); + }; + + const handleAdd = () => { + setType('create'); + configurableDrawerRef.current?.open(); + }; + const handleSubmit = useCallback( + async (values: NotifyTemplateVO) => { + if (type === 'create') { + await createNotifyTemplate(values); + message.success('创建成功'); + } else { + await updateNotifyTemplate({ + ...values, + id: currentItem!.id, + }); + message.success('编辑成功'); + } + tableRef.current?.reload(); + return true; + }, + [type, currentItem], + ); + + const handleTestSubmit = useCallback( + async (values: NotifyTemplateVO) => { + if (currentItem?.status === 1) { + return message.error('请先开启状态'); + } + const res = await sendNotify({ + ...currentItem, + ...values, + mobile: '', + templateParams: new Map(), + templateCode: currentItem!.code, + }); + message.success('提交发送成功!发送结果,见发送日志编号:' + res); + tableRef.current?.reload(); + return true; + }, + [type, currentItem], + ); + const toolbarActions: ToolbarAction[] = [ + { + key: 'add', + label: '新建', + type: 'primary', + icon: , + onClick: handleAdd, + }, + ]; + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 120, + render: ( + text: React.ReactNode, + record: NotifyTemplateVO, + _: number, + action: ProCoreActionType | undefined, + ) => [ + handleEdit(record)}> + 编辑 + , + handleSend(record)}> + 测试 + , + { + await deleteNotifyTemplate(record.id); + message.success('删除成功'); + action?.reload?.(); + }} + okText="是" + cancelText="否" + > + + 删除 + + , + ], + }; + const columns = [...baseTenantColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + headerTitle="登录日志" + showIndex={false} + toolbarActions={toolbarActions} + showSelection={false} + /> + + + + ); +}; + +export default SyStemMessageNotifyTemplate; diff --git a/src/pages/system/messages/sms/channel/config.tsx b/src/pages/system/messages/sms/channel/config.tsx new file mode 100644 index 0000000..9127ea4 --- /dev/null +++ b/src/pages/system/messages/sms/channel/config.tsx @@ -0,0 +1,105 @@ +import type { + ProColumns, + ProDescriptionsItemProps, +} from '@ant-design/pro-components'; +import dayjs from 'dayjs'; +import type { LoginLogVO } from '@/services/system/log/login'; +export const baseTenantColumns: ProColumns[] = [ + { + title: '日志编号', + dataIndex: 'id', + tip: '日志编号', + width: 100, + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '操作类型', + dataIndex: 'logType', + hideInSearch: true, // 在搜索表单中隐藏 + tip: '操作类型', // 提示信息 + }, + { + title: '操作模块', + dataIndex: 'type', + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '用户名称', + dataIndex: 'username', + }, + { + title: '登录地址', + dataIndex: 'userIp', + }, + + { + title: '浏览器', + dataIndex: 'userAgent', + hideInSearch: true, // 在搜索表单中隐藏 + }, + { + title: '登录结果', + dataIndex: 'result', + hideInSearch: true, + }, + { + title: '登录日期', + dataIndex: 'createTime', + valueType: 'dateRange', + search: { + transform: (value) => { + return { + [`createTime[0]`]: dayjs(value[0]) + .startOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + [`createTime[1]`]: dayjs(value[1]) + .endOf('day') + .format('YYYY-MM-DD HH:mm:ss'), + }; + }, + }, + render: (_, record: LoginLogVO) => + dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss'), + }, +]; + +export const descriptionsColumns = (): ProDescriptionsItemProps< + Record, + 'text' +>[] => [ + { + title: '日志编号', + key: 'id', + dataIndex: 'id', + }, + { + title: '操作类型', + key: 'logType', + dataIndex: 'logType', + }, + { + title: '用户名称', + key: 'username', + dataIndex: 'username', + }, + { + title: '登录地址', + key: 'userIp', + dataIndex: 'userIp', + }, + { + title: '浏览器', + key: 'userAgent', + dataIndex: 'userAgent', + }, + { + title: '登录结果', + key: 'result', + dataIndex: 'result', + }, + { + title: '登录日期', + key: 'createTime', + dataIndex: 'createTime', + }, +]; diff --git a/src/pages/system/messages/sms/channel/index.tsx b/src/pages/system/messages/sms/channel/index.tsx new file mode 100644 index 0000000..01d36c4 --- /dev/null +++ b/src/pages/system/messages/sms/channel/index.tsx @@ -0,0 +1,67 @@ +import type { ActionType, ProColumns } from '@ant-design/pro-components'; +import React, { useRef } from 'react'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import ModalDescriptions, { + type DescriptionsFormRef, +} from '@/components/ModalDescriptions'; +import { getLoginLogPage, type LoginLogVO } from '@/services/system/log/login'; +import { baseTenantColumns, descriptionsColumns } from './config'; + +const SyStemLogLogin = () => { + const tableRef = useRef(null); + const descriptionsRef = useRef(null); + const onFetch = async ( + params: LoginLogVO & { + pageSize?: number; + current?: number; + }, + ) => { + const data = await getLoginLogPage({ + ...params, + pageNo: params.current, + pageSize: params.pageSize, + }); + return { + data: data.list, + success: true, + total: data.total, + }; + }; + + const handleDetail = (record: LoginLogVO) => { + descriptionsRef.current?.open(record); + }; + + const actionColumns: ProColumns = { + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', + width: 80, + render: (text: React.ReactNode, record: LoginLogVO) => [ + handleDetail(record)}> + 详情 + , + ], + }; + const columns = [...baseTenantColumns, actionColumns]; + return ( + <> + + ref={tableRef} + columns={columns} + request={onFetch} + headerTitle="登录日志" + showIndex={false} + showSelection={false} + /> + + + ); +}; + +export default SyStemLogLogin; diff --git a/src/pages/system/tenant/list/index.tsx b/src/pages/system/tenant/list/index.tsx index c69b4fc..9a9859f 100644 --- a/src/pages/system/tenant/list/index.tsx +++ b/src/pages/system/tenant/list/index.tsx @@ -1,25 +1,24 @@ -import EnhancedProTable from "@/components/EnhancedProTable"; +import { PlusOutlined } from '@ant-design/icons'; +import type { ActionType, ProColumns } from '@ant-design/pro-components'; +import { Modal, Popconfirm } from 'antd'; +import dayjs from 'dayjs'; +import { ref } from 'process'; +import React, { createContext, useCallback, useRef, useState } from 'react'; +import ConfigurableDrawerForm, { + type ConfigurableDrawerFormRef, +} from '@/components/DrawerForm'; +import EnhancedProTable from '@/components/EnhancedProTable'; +import type { ToolbarAction } from '@/components/EnhancedProTable/types'; +import { formStatusType } from '@/constants'; import { - getTenantPage, createTenant, + deleteTenant, + getTenantPage, type TenantPageReqVO, type TenantVO, - deleteTenant, updateTenant, -} from "@/services/system/tenant/list"; -import React, { createContext, useCallback } from "react"; -import ConfigurableDrawerForm, { - ConfigurableDrawerFormRef, -} from "@/components/DrawerForm"; -import { useRef, useState } from "react"; -import { formColumns, baseTenantColumns } from "./config"; -import { PlusOutlined } from "@ant-design/icons"; -import { ref } from "process"; -import { ActionType, ProColumns } from "@ant-design/pro-components"; -import { ToolbarAction } from "@/components/EnhancedProTable/types"; -import { Modal, Popconfirm } from "antd"; -import { formStatusType } from "@/constants"; -import dayjs from "dayjs"; +} from '@/services/system/tenant/list'; +import { baseTenantColumns, formColumns } from './config'; export const waitTimePromise = async (time: number = 90) => { return new Promise((resolve) => { setTimeout(() => { @@ -30,13 +29,13 @@ export const waitTimePromise = async (time: number = 90) => { const TenantList = () => { const configurableDrawerRef = useRef(null); const tableRef = useRef(null); - const [type, setType] = useState<"create" | "update">("create"); + const [type, setType] = useState<'create' | 'update'>('create'); const [id, setId] = useState(0); const onFetch = async ( params: TenantPageReqVO & { pageSize?: number; current?: number; - } + }, ) => { await waitTimePromise(); const data = await getTenantPage({ @@ -53,7 +52,7 @@ const TenantList = () => { const handleSubmit = useCallback( async (values: TenantVO) => { - if (type === "create") { + if (type === 'create') { await createTenant(values); } else { await updateTenant({ @@ -65,34 +64,34 @@ const TenantList = () => { tableRef.current?.reload(); return true; }, - [type] + [type, id], ); const handleAdd = () => { - setType("create"); + setType('create'); configurableDrawerRef.current?.open({}); }; const handleEdit = (record: TenantVO) => { - setType("update"); + setType('update'); setId(record.id); configurableDrawerRef.current?.open(record); }; const toolbarActions: ToolbarAction[] = [ { - key: "add", - label: "新建", - type: "primary", + key: 'add', + label: '新建', + type: 'primary', icon: , onClick: handleAdd, }, ]; const actionColumns: ProColumns = { - title: "操作", - dataIndex: "option", - valueType: "option", - fixed: "right", + title: '操作', + dataIndex: 'option', + valueType: 'option', + fixed: 'right', width: 120, render: (text: React.ReactNode, record: TenantVO, _: any, action: any) => [ handleEdit(record)}> diff --git a/src/requestErrorConfig.ts b/src/requestErrorConfig.ts index 54187f5..1b1dc75 100644 --- a/src/requestErrorConfig.ts +++ b/src/requestErrorConfig.ts @@ -1,14 +1,15 @@ -import type { RequestOptions } from "@@/plugin-request/request"; -import type { RequestConfig } from "@umijs/max"; -import { message, notification } from "antd"; -import { deleteUserCache } from "@/hooks/web/useCache"; +import type { RequestOptions } from '@@/plugin-request/request'; +import type { RequestConfig } from '@umijs/max'; +import { request } from '@umijs/max'; +import { message, notification } from 'antd'; +import { deleteUserCache } from '@/hooks/web/useCache'; import { getAccessToken, getRefreshToken, getTenantId, setToken, -} from "./utils/auth"; -import { request } from "@umijs/max"; +} from './utils/auth'; + const tenantEnable = process.env.VITE_APP_TENANT_ENABLE; // const { result_code, base_url, request_timeout } = config; // 错误处理方案: 错误类型 @@ -27,22 +28,25 @@ interface ResponseStructure { msg?: string; } const ignoreMsgs = [ - "无效的刷新令牌", // 刷新令牌被删除时,不用提示 - "刷新令牌已过期", // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401,无法跳转到登出界面 + '无效的刷新令牌', // 刷新令牌被删除时,不用提示 + '刷新令牌已过期', // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401,无法跳转到登出界面 ]; + +import { EventEmitter } from 'events'; +export const requestEventBus = new EventEmitter(); const errorCode: { [key: string]: string } = { - "400": "请求参数不正确", - "401": "账号未登录", - "403": "当前操作没有权限", - "404": "访问资源不存在", - "405": "请求方法不正确", - "423": "请求失败,请稍后重试", - "429": "请求失败,请稍后重试", - "500": "系统异常", - "501": "功能未实现/未开启", - "502": "错误的配置项", - "900": "重复请求,请稍后重试", - default: "系统未知错误,请反馈给管理员", + '400': '请求参数不正确', + '401': '账号未登录', + '403': '当前操作没有权限', + '404': '访问资源不存在', + '405': '请求方法不正确', + '423': '请求失败,请稍后重试', + '429': '请求失败,请稍后重试', + '500': '系统异常', + '501': '功能未实现/未开启', + '502': '错误的配置项', + '900': '重复请求,请稍后重试', + default: '系统未知错误,请反馈给管理员', }; /** @@ -52,8 +56,8 @@ const errorCode: { [key: string]: string } = { */ const refreshToken = async () => { - return await request("/system/auth/refresh-token", { - method: "POST", + return await request('/system/auth/refresh-token', { + method: 'POST', params: { refreshToken: getRefreshToken() }, }); }; @@ -87,7 +91,7 @@ const handleAuthorized = () => { // 是否正在刷新中 let isRefreshToken = false; // 请求队列 -let requestList: any[] = []; +const requestList: any[] = []; export const errorConfig: RequestConfig = { // 错误处理: umi@3 的错误处理方案。 errorConfig: { @@ -96,7 +100,7 @@ export const errorConfig: RequestConfig = { const { success, data, code, msg } = res as unknown as ResponseStructure; if (!success) { const error: any = new Error(msg); - error.name = "BizError"; + error.name = 'BizError'; error.info = { code, msg, data }; throw error; // 抛出自制的错误 } @@ -105,9 +109,9 @@ export const errorConfig: RequestConfig = { errorHandler: async (error: any, opts: any) => { if (opts?.skipErrorHandler) throw error; // 我们的 errorThrower 抛出的错误。 - console.log("errorHandler", error); + console.log('errorHandler', error); const errorInfo: ResponseStructure | undefined = error.info; - if (error.name === "BizError") { + if (error.name === 'BizError') { if (errorInfo) { const { msg, code } = errorInfo; message.error(msg); @@ -117,9 +121,9 @@ export const errorConfig: RequestConfig = { // 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围 message.error(`Response status:${error.response.status}`); } else if (error.request) { - message.error("None response! Please retry."); + message.error('None response! Please retry.'); } else { - message.error("发送请求时出了点问题:" + error.msg); + message.error('发送请求时出了点问题:' + error.msg); } }, }, @@ -146,8 +150,8 @@ export const errorConfig: RequestConfig = { // 未设置状态码则默认成功状态 // 二进制数据则直接返回,例如说 Excel 导出 if ( - response.request.responseType === "blob" || - response.request.responseType === "arraybuffer" + response.request.responseType === 'blob' || + response.request.responseType === 'arraybuffer' ) { // 注意:如果导出的响应为 json,说明可能失败了,不直接返回进行下载 // if (response.data.type !== "application/json") { @@ -155,14 +159,14 @@ export const errorConfig: RequestConfig = { // } data = await new Response(data).json(); } - // 获取错误信息 - const msg = data.msg || errorCode[code] || errorCode["default"]; - if (ignoreMsgs.indexOf(msg) !== -1) { - // 如果是忽略的错误码,直接返回 msg 异常 - return Promise.reject(msg); - } + // // 获取错误信息 + // const msg = data.msg || errorCode[code] || errorCode["default"]; + // if (ignoreMsgs.indexOf(msg) !== -1) { + // // 如果是忽略的错误码,直接返回 msg 异常 + // return Promise.reject(msg); + // } // 发送请求时出了点问题 - else if (code === 401) { + if (code === 401) { if (!isRefreshToken) { isRefreshToken = true; // 1. 如果获取不到刷新令牌,则只能执行登出操作 @@ -172,34 +176,25 @@ export const errorConfig: RequestConfig = { // 2. 进行刷新访问令牌 try { const refreshTokenRes = await refreshToken(); - console.log("刷新成功", refreshTokenRes); + console.log('刷新成功', refreshTokenRes); // 2.1 刷新成功,则回放队列的请求 + 当前请求 setToken(refreshTokenRes); - console.log(getAccessToken()); - config.headers!.Authorization = "Bearer " + getAccessToken(); - console.log(requestList); - requestList.forEach((cb: any) => { - cb(); - }); + // 发出 token 刷新事件 + requestEventBus.emit('token-refreshed'); } catch (e) { // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。 - // 2.2 刷新失败,只回放队列的请求 - requestList.forEach((cb: any) => { - cb(); - }); // 提示是否要登出。即不回放当前请求!不然会形成递归 return handleAuthorized(); } finally { - requestList = []; isRefreshToken = false; } return request(config.url!, config); } else { - console.log("刷新令牌失败"); + console.log('刷新令牌失败'); //添加到队列,等待刷新获取到新的令牌 return new Promise((resolve) => { requestList.push(() => { - config.headers!.Authorization = "Bearer " + getAccessToken(); // 让每个请求携带自定义token 请根据实际情况自行修改 + config.headers!.Authorization = 'Bearer ' + getAccessToken(); // 让每个请求携带自定义token 请根据实际情况自行修改 resolve(request(config.url!, config)); }); }); @@ -209,7 +204,7 @@ export const errorConfig: RequestConfig = { return Promise.reject(data); } if (data?.success === false) { - message.error("请求失败!"); + message.error('请求失败!'); return Promise.reject(data); } diff --git a/src/services/system/log/login.ts b/src/services/system/log/login.ts new file mode 100644 index 0000000..a9a0daf --- /dev/null +++ b/src/services/system/log/login.ts @@ -0,0 +1,27 @@ +import { request } from "@umijs/max"; + +export interface LoginLogVO { + id: number; + logType: number; + traceId: number; + userId: number; + userType: number; + username: string; + result: number; + status: number; + userIp: string; + userAgent: string; + createTime: Date; +} + +// 查询登录日志列表 +export async function getLoginLogPage(params: PageParam) { + return request("/system/login-log/page", { + method: "GET", + params, + }); +} +// // 导出登录日志 +// export const exportLoginLog = (params) => { +// return request.download({ url: '/system/login-log/export', params }) +// } diff --git a/src/services/system/log/operate.ts b/src/services/system/log/operate.ts index 8c269f1..fe88d65 100644 --- a/src/services/system/log/operate.ts +++ b/src/services/system/log/operate.ts @@ -20,11 +20,6 @@ export type OperateLogVO = { createTime: Date; }; -// 查询操作日志列表 -// export const getOperateLogPage = (params: PageParam) => { -// return request.get({ url: '/system/operate-log/page', params }) -// } - export async function getOperateLogPage(params: PageParam) { return request("/system/operate-log/page", { method: "GET", diff --git a/src/services/system/message/mail/account.tsx b/src/services/system/message/mail/account.tsx new file mode 100644 index 0000000..e7730cd --- /dev/null +++ b/src/services/system/message/mail/account.tsx @@ -0,0 +1,72 @@ +import { request } from "@umijs/max"; + +export interface MailAccountVO { + id: number; + mail: string; + username: string; + password: string; + host: string; + port: number; + sslEnable: boolean; + starttlsEnable: boolean; +} + +// 查询邮箱账号列表 +// export const getMailAccountPage = async (params: PageParam) => { +// return await request.get({ url: '/system/mail-account/page', params }) +// } +export async function getMailAccountPage(params: PageParam) { + return request("/system/mail-account/page", { + method: "GET", + params, + }); +} +// 查询邮箱账号详情 +// export const getMailAccount = async (id: number) => { +// return await request.get({ url: '/system/mail-account/get?id=' + id }) +// } +export async function getMailAccount(id: number) { + return request("/system/mail-account/get", { + method: "GET", + params: { id }, + }); +} +// 新增邮箱账号 +// export const createMailAccount = async (data: MailAccountVO) => { +// return await request.post({ url: "/system/mail-account/create", data }); +// }; +export async function createMailAccount(data: MailAccountVO) { + return request("/system/mail-account/create", { + method: "POST", + data, + }); +} +// 修改邮箱账号 +// export const updateMailAccount = async (data: MailAccountVO) => { +// return await request.put({ url: "/system/mail-account/update", data }); +// }; +export async function updateMailAccount(data: MailAccountVO) { + return request("/system/mail-account/update", { + method: "PUT", + data, + }); +} +// 删除邮箱账号 +// export const deleteMailAccount = async (id: number) => { +// return await request.delete({ url: "/system/mail-account/delete?id=" + id }); +// }; +export async function deleteMailAccount(id: number) { + return request("/system/mail-account/delete", { + method: "DELETE", + params: { id }, + }); +} +// 获得邮箱账号精简列表 +// export const getSimpleMailAccountList = async () => { +// return request.get({ url: '/system/mail-account/simple-list' }) +// } +export async function getSimpleMailAccountList() { + return request("/system/mail-account/simple-list", { + method: "GET", + }); +} diff --git a/src/services/system/message/mail/log.tsx b/src/services/system/message/mail/log.tsx new file mode 100644 index 0000000..8a21a32 --- /dev/null +++ b/src/services/system/message/mail/log.tsx @@ -0,0 +1,41 @@ +import { request } from "@umijs/max"; + +export interface MailLogVO { + id: number; + userId: number; + userType: number; + toMail: string; + accountId: number; + fromMail: string; + templateId: number; + templateCode: string; + templateNickname: string; + templateTitle: string; + templateContent: string; + templateParams: string; + sendStatus: number; + sendTime: Date; + sendMessageId: string; + sendException: string; +} + +// 查询邮件日志列表 +// export const getMailLogPage = async (params: PageParam) => { +// return await request.get({ url: '/system/mail-log/page', params }) +// } +export async function getMailLogPage(params: PageParam) { + return request("/system/mail-log/page", { + method: "GET", + params, + }); +} +// 查询邮件日志详情 +// export const getMailLog = async (id: number) => { +// return await request.get({ url: '/system/mail-log/get?id=' + id }) +// } +export async function getMailLog(id: number) { + return request("/system/mail-log/get", { + method: "GET", + params: { id }, + }); +} diff --git a/src/services/system/message/mail/template.tsx b/src/services/system/message/mail/template.tsx new file mode 100644 index 0000000..e7bb294 --- /dev/null +++ b/src/services/system/message/mail/template.tsx @@ -0,0 +1,81 @@ +import { request } from "@umijs/max"; + +export interface MailTemplateVO { + id: number; + name: string; + code: string; + accountId: number; + nickname: string; + title: string; + content: string; + params: string; + status: number; + remark: string; +} + +export interface MailSendReqVO { + mail: string; + templateCode: string; + templateParams: Map; +} + +// 查询邮件模版列表 +// export const getMailTemplatePage = async (params: PageParam) => { +// return await request.get({ url: '/system/mail-template/page', params }) +// } +export async function getMailTemplatePage(params: PageParam) { + return request("/system/mail-template/page", { + method: "GET", + params, + }); +} +// 查询邮件模版详情 +// export const getMailTemplate = async (id: number) => { +// return await request.get({ url: '/system/mail-template/get?id=' + id }) +// } +export async function getMailTemplate(id: number) { + return request("/system/mail-template/get", { + method: "GET", + params: { id }, + }); +} +// 新增邮件模版 +// export const createMailTemplate = async (data: MailTemplateVO) => { +// return await request.post({ url: '/system/mail-template/create', data }) +// } +export async function createMailTemplate(data: MailTemplateVO) { + return request("/system/mail-template/create", { + method: "POST", + data, + }); +} +// 修改邮件模版 +// export const updateMailTemplate = async (data: MailTemplateVO) => { +// return await request.put({ url: '/system/mail-template/update', data }) +// } +export async function updateMailTemplate(data: MailTemplateVO) { + return request("/system/mail-template/update", { + method: "PUT", + data, + }); +} +// 删除邮件模版 +// export const deleteMailTemplate = async (id: number) => { +// return await request.delete({ url: '/system/mail-template/delete?id=' + id }) +// } +export async function deleteMailTemplate(id: number) { + return request("/system/mail-template/delete", { + method: "PUT", + params: { id }, + }); +} +// 发送邮件 +// export const sendMail = (data: MailSendReqVO) => { +// return request.post({ url: '/system/mail-template/send-mail', data }) +// } +export async function sendMail(data: MailTemplateVO) { + return request("/system/mail-template/send-mail", { + method: "PUT", + data, + }); +} diff --git a/src/services/system/message/notice.tsx b/src/services/system/message/notice.tsx new file mode 100644 index 0000000..af32742 --- /dev/null +++ b/src/services/system/message/notice.tsx @@ -0,0 +1,74 @@ +import { request } from "@umijs/max"; + +export interface NoticeVO { + id: number; + title: string; + type: number; + content: string; + status: number; + remark: string; + creator: string; + createTime: Date; +} + +// 查询公告列表 +// export const getNoticePage = (params: PageParam) => { +// return request.get({ url: '/system/notice/page', params }) +// } +export async function getNoticePage(params: PageParam) { + return request("/system/notice/page", { + method: "GET", + params, + }); +} +// 查询公告详情 +// export const getNotice = (id: number) => { +// return request.get({ url: '/system/notice/get?id=' + id }) +// } +export async function getNotice(id: number) { + return request("/system/notice/get", { + method: "GET", + params: { id }, + }); +} +// 新增公告 +// export const createNotice = (data: NoticeVO) => { +// return request.post({ url: '/system/notice/create', data }) +// } +export async function createNotice(data: NoticeVO) { + return request("/system/notice/create", { + method: "POST", + data, + }); +} +// 修改公告 +// export const updateNotice = (data: NoticeVO) => { +// return request.put({ url: '/system/notice/update', data }) +// } +export async function updateNotice(data: NoticeVO) { + return request("/system/notice/update", { + method: "PUT", + data, + }); +} +// 删除公告 +// export const deleteNotice = (id: number) => { +// return request.delete({ url: '/system/notice/delete?id=' + id }) +// } +export async function deleteNotice(id: number) { + return request("/system/notice/update", { + method: "DELETE", + params: { id }, + }); +} +// 推送公告 +// export const pushNotice = (id: number) => { +// return request.post({ url: '/system/notice/push?id=' + id }) +// } + +export async function pushNotice(id: number) { + return request("/system/notice/push", { + method: "POST", + params: { id }, + }); +} diff --git a/src/services/system/message/notify/message.tsx b/src/services/system/message/notify/message.tsx new file mode 100644 index 0000000..594af59 --- /dev/null +++ b/src/services/system/message/notify/message.tsx @@ -0,0 +1,76 @@ +import { request } from "@umijs/max"; + +export interface NotifyMessageVO { + id: number; + userId: number; + userType: number; + templateId: number; + templateCode: string; + templateNickname: string; + templateContent: string; + templateType: number; + templateParams: string; + readStatus: boolean; + readTime: Date; + createTime: Date; +} + +// 查询站内信消息列表 +// export const getNotifyMessagePage = async (params: PageParam) => { +// return await request.get({ url: '/system/notify-message/page', params }) +// } +export async function getNotifyMessagePage(params: PageParam) { + return request("/system/notify-message/page", { + method: "GET", + params, + }); +} +// 获得我的站内信分页 +// export const getMyNotifyMessagePage = async (params: PageParam) => { +// return await request.get({ url: '/system/notify-message/my-page', params }) +// } +export async function getMyNotifyMessagePage(params: PageParam) { + return request("/system/notify-message/my-page", { + method: "GET", + params, + }); +} +// 批量标记已读 +// export const updateNotifyMessageRead = async (ids) => { +// return await request.put({ +// url: '/system/notify-message/update-read?' + qs.stringify({ ids: ids }, { indices: false }) +// }) +// } +export async function updateNotifyMessageRead(ids: number[]) { + return request("/system/notice/update", { + method: "PUT", + params: { ids: ids }, + }); +} +// 标记所有站内信为已读 +// export const updateAllNotifyMessageRead = async () => { +// return await request.put({ url: '/system/notify-message/update-all-read' }) +// } +export async function updateAllNotifyMessageRead() { + return request("/system/notify-message/update-all-read", { + method: "PUT", + }); +} +// 获取当前用户的最新站内信列表 +// export const getUnreadNotifyMessageList = async () => { +// return await request.get({ url: '/system/notify-message/get-unread-list' }) +// } +export async function getUnreadNotifyMessageList() { + return request("/system/notify-message/get-unread-list", { + method: "GET", + }); +} +// 获得当前用户的未读站内信数量 +// export const getUnreadNotifyMessageCount = async () => { +// return await request.get({ url: '/system/notify-message/get-unread-count' }) +// } +export async function getUnreadNotifyMessageCount() { + return request("/system/notify-message/get-unread-count", { + method: "GET", + }); +} diff --git a/src/services/system/message/notify/template.tsx b/src/services/system/message/notify/template.tsx new file mode 100644 index 0000000..fa70276 --- /dev/null +++ b/src/services/system/message/notify/template.tsx @@ -0,0 +1,80 @@ +import { request } from "@umijs/max"; + +export interface NotifyTemplateVO { + id: number; + name: string; + nickname: string; + code: string; + content: string; + type?: number; + params: string; + status: number; + remark: string; + mobile: string; + createTime: Date; + userType: number; + userId: number | null; + templateCode: string; + templateParams: Map; +} + +// 查询站内信模板列表 +// export const getNotifyTemplatePage = async (params: PageParam) => { +// return await request.get({ url: 'getNotifyTemplatePage', params }) +// } +export async function getNotifyTemplatePage(params: PageParam) { + return request("/system/notify-template/page", { + method: "GET", + params, + }); +} +// 查询站内信模板详情 +// export const getNotifyTemplate = async (id: number) => { +// return await request.get({ url: '/system/notify-template/get?id=' + id }) +// } +export async function getNotifyTemplate(id: number) { + return request("/system/notify-template/get", { + method: "GET", + params: { id }, + }); +} +// 新增站内信模板 +// export const createNotifyTemplate = async (data: NotifyTemplateVO) => { +// return await request.post({ url: '/system/notify-template/create', data }) +// } +export async function createNotifyTemplate(data: NotifyTemplateVO) { + return request("/system/notify-template/create", { + method: "POST", + data, + }); +} +// 修改站内信模板 +// export const updateNotifyTemplate = async (data: NotifyTemplateVO) => { +// return await request.put({ url: '/system/notify-template/update', data }) +// } +export async function updateNotifyTemplate(data: NotifyTemplateVO) { + return request("/system/notify-template/update", { + method: "PUT", + data, + }); +} +// 删除站内信模板 +// export const deleteNotifyTemplate = async (id: number) => { +// return await request.delete({ url: '/system/notify-template/delete?id=' + id }) +// } +export async function deleteNotifyTemplate(id: number) { + return request("/system/notify-template/delete", { + method: "DELETE", + params: { id }, + }); +} +// 发送站内信 +// export const sendNotify = (data: NotifySendReqVO) => { +// return request.post({ url: '/system/notify-template/send-notify', data }) +// } +export async function sendNotify(data: NotifyTemplateVO) { + return request("/system/notify-template/send-notify", { + method: "POST", + data, + }); +} diff --git a/src/services/system/message/sms/channel.tsx b/src/services/system/message/sms/channel.tsx new file mode 100644 index 0000000..558ea8c --- /dev/null +++ b/src/services/system/message/sms/channel.tsx @@ -0,0 +1,73 @@ +import { request } from "@umijs/max"; + +export interface SmsChannelVO { + id: number; + code: string; + status: number; + signature: string; + remark: string; + apiKey: string; + apiSecret: string; + callbackUrl: string; + createTime: Date; +} + +// 查询短信渠道列表 +// export const getSmsChannelPage = (params: PageParam) => { +// return request.get({ url: '/system/sms-channel/page', params }) +// } +export async function getSmsChannelPage(params: PageParam) { + return request("/system/sms-channel/page", { + method: "GET", + params, + }); +} +// 获得短信渠道精简列表 +// export function getSimpleSmsChannelList() { +// return request.get({ url: '/system/sms-channel/simple-list' }) +// } +export async function getSimpleSmsChannelList() { + return request("/system/sms-channel/simple-list", { + method: "GET", + }); +} +// 查询短信渠道详情 +// export const getSmsChannel = (id: number) => { +// return request.get({ url: '/system/sms-channel/get?id=' + id }) +// } +export async function getSmsChannel(id: number) { + return request("/system/sms-channel/get", { + method: "GET", + params: { id }, + }); +} +// 新增短信渠道 +// export const createSmsChannel = (data: SmsChannelVO) => { +// return request.post({ url: '/system/sms-channel/create', data }) +// } +export async function createSmsChannel(data: SmsChannelVO) { + return request("/system/sms-channel/create", { + method: "POST", + data, + }); +} +// 修改短信渠道 +// export const updateSmsChannel = (data: SmsChannelVO) => { +// return request.put({ url: '/system/sms-channel/update', data }) +// } +export async function updateSmsChannel(data: SmsChannelVO) { + return request("/system/sms-channel/create", { + method: "PUT", + data, + }); +} +// 删除短信渠道 +// export const deleteSmsChannel = (id: number) => { +// return request.delete({ url: '/system/sms-channel/delete?id=' + id }) +// } +export async function deleteSmsChannel(id: number) { + return request("/system/sms-channel/create", { + method: "DELETE", + params: { id }, + }); +} diff --git a/src/services/system/message/sms/log.tsx b/src/services/system/message/sms/log.tsx new file mode 100644 index 0000000..c70e650 --- /dev/null +++ b/src/services/system/message/sms/log.tsx @@ -0,0 +1,40 @@ +import { request } from "@umijs/max"; + +export interface SmsLogVO { + id: number | null; + channelId: number | null; + channelCode: string; + templateId: number | null; + templateCode: string; + templateType: number | null; + templateContent: string; + templateParams: Map | null; + apiTemplateId: string; + mobile: string; + userId: number | null; + userType: number | null; + sendStatus: number | null; + sendTime: Date | null; + apiSendCode: string; + apiSendMsg: string; + apiRequestId: string; + apiSerialNo: string; + receiveStatus: number | null; + receiveTime: Date | null; + apiReceiveCode: string; + apiReceiveMsg: string; + createTime: Date | null; +} + +// 查询短信日志列表 + +export async function getSmsLogPage(params: PageParam) { + return request("/system/sms-channel/page", { + method: "GET", + params, + }); +} +// 导出短信日志 +// export const exportSmsLog = (params) => { +// return request.download({ url: '/system/sms-log/export-excel', params }) +// } diff --git a/src/services/system/message/sms/template.tsx b/src/services/system/message/sms/template.tsx new file mode 100644 index 0000000..c1080db --- /dev/null +++ b/src/services/system/message/sms/template.tsx @@ -0,0 +1,92 @@ +import { request } from "@umijs/max"; + +export interface SmsTemplateVO { + id?: number; + type?: number; + status: number; + code: string; + name: string; + content: string; + remark: string; + apiTemplateId: string; + channelId?: number; + channelCode?: string; + params?: string[]; + createTime?: Date; +} + +export interface SendSmsReqVO { + mobile: string; + templateCode: string; + templateParams: Map; +} + +// 查询短信模板列表 +// export const getSmsTemplatePage = (params: PageParam) => { +// return request.get({ url: '/system/sms-template/page', params }) +// } +export async function getSmsTemplatePage(params: PageParam) { + return request("/system/sms-template/page", { + method: "GET", + params, + }); +} +// 查询短信模板详情 +// export const getSmsTemplate = (id: number) => { +// return request.get({ url: '/system/sms-template/get?id=' + id }) +// } +export async function getSmsTemplate(id: number) { + return request("/system/sms-template/get", { + method: "GET", + params: { id }, + }); +} +// 新增短信模板 +// export const createSmsTemplate = (data: SmsTemplateVO) => { +// return request.post({ url: '/system/sms-template/create', data }) +// } +export async function createSmsTemplate(data: SmsTemplateVO) { + return request("/system/sms-template/create", { + method: "POST", + data, + }); +} +// 修改短信模板 +// export const updateSmsTemplate = (data: SmsTemplateVO) => { +// return request.put({ url: '/system/sms-template/update', data }) +// } +export async function updateSmsTemplate(data: SmsTemplateVO) { + return request("/system/sms-template/update", { + method: "PUT", + data, + }); +} +// 删除短信模板 +// export const deleteSmsTemplate = (id: number) => { +// return request.delete({ url: '/system/sms-template/delete?id=' + id }) +// } +export async function deleteSmsTemplate(id: number) { + return request("/system/sms-template/update", { + method: "DELETE", + params: { id }, + }); +} +// 导出短信模板 +// export const exportSmsTemplate = (params) => { +// return request.download({ +// url: '/system/sms-template/export-excel', +// params +// }) +// } + +// 发送短信 +// export const sendSms = (data: SendSmsReqVO) => { +// return request.post({ url: '/system/sms-template/send-sms', data }) +// } + +export async function sendSms(data: SmsTemplateVO) { + return request("/system/sms-template/create", { + method: "POST", + data, + }); +}