diff --git a/server/REST API/租户管理API.http b/server/REST API/租户管理API.http index 4175d23..1c6733c 100644 --- a/server/REST API/租户管理API.http +++ b/server/REST API/租户管理API.http @@ -5,7 +5,7 @@ x-tenant-id: {{tenantId}} Authorization: Bearer {{tokenValue}} { - "tenantId": "baidu", + "tenantId": "baidu120", "logo": "https://baidu.com/baidu.png", "name": "百度科技有限公司", "email": "baidu@qq.com", diff --git a/server/REST API/认证授权API.http b/server/REST API/认证授权API.http index c643684..980bfe5 100644 --- a/server/REST API/认证授权API.http +++ b/server/REST API/认证授权API.http @@ -16,7 +16,7 @@ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:86.0) Gecko/20100101 Fi "username": "admin", "password": "123456", "codeKey": "5jXzuwcoUzbtnHNh", - "codeText": "re6e" + "codeText": "jap5" } > {% diff --git a/server/molly-cms/src/main/java/com/xaaef/molly/corems/api/impl/ApiCmsProjectServiceImpl.java b/server/molly-cms/src/main/java/com/xaaef/molly/corems/api/impl/ApiCmsProjectServiceImpl.java index f7f28c0..8e32047 100644 --- a/server/molly-cms/src/main/java/com/xaaef/molly/corems/api/impl/ApiCmsProjectServiceImpl.java +++ b/server/molly-cms/src/main/java/com/xaaef/molly/corems/api/impl/ApiCmsProjectServiceImpl.java @@ -66,7 +66,7 @@ public class ApiCmsProjectServiceImpl implements ApiCmsProjectService { delegate(po.getTenantId(), () -> { var project = new CmsProject() .setProjectId(10001L) - .setProjectName(po.getName()) + .setProjectName("默认项目") .setLinkman(po.getLinkman()) .setContactNumber(po.getContactNumber()) .setAreaCode(po.getAreaCode()) diff --git a/server/molly-cms/src/main/java/com/xaaef/molly/corems/service/impl/CmsDeviceServiceImpl.java b/server/molly-cms/src/main/java/com/xaaef/molly/corems/service/impl/CmsDeviceServiceImpl.java index df168a6..b31fcc5 100644 --- a/server/molly-cms/src/main/java/com/xaaef/molly/corems/service/impl/CmsDeviceServiceImpl.java +++ b/server/molly-cms/src/main/java/com/xaaef/molly/corems/service/impl/CmsDeviceServiceImpl.java @@ -24,6 +24,10 @@ import org.springframework.stereotype.Service; @AllArgsConstructor public class CmsDeviceServiceImpl extends BaseServiceImpl implements CmsDeviceService { - + @Override + public boolean save(CmsDevice entity) { + entity.setDeviceId(null); + return super.save(entity); + } } diff --git a/server/molly-cms/src/main/java/com/xaaef/molly/corems/service/impl/CmsProjectServiceImpl.java b/server/molly-cms/src/main/java/com/xaaef/molly/corems/service/impl/CmsProjectServiceImpl.java index fb45cdf..ed3d779 100644 --- a/server/molly-cms/src/main/java/com/xaaef/molly/corems/service/impl/CmsProjectServiceImpl.java +++ b/server/molly-cms/src/main/java/com/xaaef/molly/corems/service/impl/CmsProjectServiceImpl.java @@ -1,8 +1,6 @@ package com.xaaef.molly.corems.service.impl; import cn.hutool.core.util.StrUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -12,7 +10,6 @@ import com.xaaef.molly.corems.mapper.CmsProjectMapper; import com.xaaef.molly.corems.service.CmsProjectService; import com.xaaef.molly.corems.vo.ResetPasswordVO; import com.xaaef.molly.internal.api.ApiPmsDeptService; -import com.xaaef.molly.internal.dto.PmsDeptDTO; import com.xaaef.molly.tenant.base.service.impl.BaseServiceImpl; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -20,7 +17,6 @@ import org.springframework.stereotype.Service; import java.util.Collection; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; import static com.xaaef.molly.auth.jwt.JwtSecurityUtils.*; diff --git a/server/plugins/mbp-tenant/src/main/java/com/xaaef/molly/tenant/schema/SchemaDataSourceManager.java b/server/plugins/mbp-tenant/src/main/java/com/xaaef/molly/tenant/schema/SchemaDataSourceManager.java index 98a51b1..a4cb0f5 100644 --- a/server/plugins/mbp-tenant/src/main/java/com/xaaef/molly/tenant/schema/SchemaDataSourceManager.java +++ b/server/plugins/mbp-tenant/src/main/java/com/xaaef/molly/tenant/schema/SchemaDataSourceManager.java @@ -8,6 +8,7 @@ import liquibase.resource.ClassLoaderResourceAccessor; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; import javax.sql.DataSource; @@ -35,6 +36,8 @@ public class SchemaDataSourceManager implements DatabaseManager { // 默认租户的数据源 private final DataSource dataSource; + private final JdbcTemplate jdbcTemplate; + private final MultiTenantProperties multiTenantProperties; private final DataSourceProperties dataSourceProperties; @@ -65,25 +68,24 @@ public class SchemaDataSourceManager implements DatabaseManager { @Override public void updateTable(String tenantId) { log.info("tenantId: {} update table ...", tenantId); + // 判断数据库是否存在!不存在就创建 + var tenantDbName = multiTenantProperties.getPrefix() + tenantId; + var sql = String.format("CREATE DATABASE IF NOT EXISTS %s CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;", tenantDbName); + jdbcTemplate.execute(sql); try { - // 判断 schema 是否存在。不存在就创建 - var conn = dataSource.getConnection(); - // 判断数据库是否存在!不存在就创建 - String tenantDbName = multiTenantProperties.getPrefix() + tenantId; - String sql = String.format("CREATE DATABASE IF NOT EXISTS %s CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;", tenantDbName); - conn.createStatement().execute(sql); // 创建一次性的 jdbc 链接。只是用来生成表结构的。用完就关闭。 - var conn1 = new JdbcConnection(getTempConnection(tenantDbName)); + var tempConn = getTempConnection(tenantDbName); + log.info("getTempConnection: {} ", tempConn); var changeLogPath = multiTenantProperties.getOtherChangeLog(); // 使用 Liquibase 创建表结构 if (multiTenantProperties.getOtherChangeLog().startsWith(CLASSPATH_URL_PREFIX)) { changeLogPath = multiTenantProperties.getOtherChangeLog().replaceFirst(CLASSPATH_URL_PREFIX, ""); } - var liquibase = new Liquibase(changeLogPath, new ClassLoaderResourceAccessor(), conn1); + var liquibase = new Liquibase(changeLogPath, new ClassLoaderResourceAccessor(), new JdbcConnection(tempConn)); // 更新 数据库 结构体 liquibase.update(); // 关闭链接 - conn1.close(); + tempConn.close(); } catch (Exception e) { e.printStackTrace(); log.error(e.getMessage()); @@ -96,14 +98,7 @@ public class SchemaDataSourceManager implements DatabaseManager { log.warn("tenantId: {} delete table ...", tenantId); String tenantDbName = multiTenantProperties.getPrefix() + tenantId; String sql = String.format("DROP DATABASE %s ;", tenantDbName); - try { - var conn = getTempConnection(tenantDbName); - conn.createStatement().execute(sql); - conn.close(); - } catch (Exception e) { - e.printStackTrace(); - log.error(e.getMessage()); - } + jdbcTemplate.execute(sql); } @@ -118,6 +113,7 @@ public class SchemaDataSourceManager implements DatabaseManager { var oldDbName = getOldDbName(dataSourceProperties.getUrl()); // 替换连接池中的数据库名称 var dataSourceUrl = dataSourceProperties.getUrl().replaceFirst(oldDbName, tenantDbName); + //3.获取数据库连接对象 return DriverManager.getConnection(dataSourceUrl, dataSourceProperties.getUsername(), diff --git a/web-ui/src/api/device.ts b/web-ui/src/api/device.ts new file mode 100644 index 0000000..b39ccd9 --- /dev/null +++ b/web-ui/src/api/device.ts @@ -0,0 +1,33 @@ +import { IJsonResult, IPageResult, ISearchQuery } from "@/types/base" +import { ICmsDevice } from "@/types/cms" +import { httpDelete, httpGet, httpPost, httpPut } from "@/utils/service" + +/** 根据Id查询 */ +export const getDeviceApi = (id: number) => { + return httpGet>(`/cms/device/${id}`) +} + +/** 分页查询所有 */ +export const queryDeviceApi = (data: ISearchQuery) => { + return httpGet>("/cms/device/query", data) +} + +/** 查询所有 */ +export const listDeviceApi = () => { + return httpGet>("/cms/device/list") +} + +/** 新增 */ +export const saveDeviceApi = (data: ICmsDevice) => { + return httpPost>("/cms/device", data) +} + +/** 修改 */ +export const updateDeviceApi = (data: ICmsDevice) => { + return httpPut>("/cms/device", data) +} + +/** 删除 */ +export const deleteDeviceApi = (id: number) => { + return httpDelete>(`/cms/device/${id}`) +} diff --git a/web-ui/src/components/SearchProject/index.vue b/web-ui/src/components/SearchProject/index.vue index 696dfaf..2aeb6d5 100644 --- a/web-ui/src/components/SearchProject/index.vue +++ b/web-ui/src/components/SearchProject/index.vue @@ -13,7 +13,14 @@ - + + + diff --git a/web-ui/src/hooks/useTenantAndProject.ts b/web-ui/src/hooks/useTenantAndProject.ts new file mode 100644 index 0000000..4cc11ec --- /dev/null +++ b/web-ui/src/hooks/useTenantAndProject.ts @@ -0,0 +1,17 @@ +import { computed } from "vue" +import { useProjectStoreHook } from "@/store/modules/project" +import { useTenantStoreHook } from "@/store/modules/tenant" + +const projectStore = useProjectStoreHook() +const tenantStore = useTenantStoreHook() + +// 租户ID 和 项目 组成的 唯一ID +const currentOnlyId = computed(() => { + return tenantStore.getCurrentTenantId() + projectStore.getCurrentProjectId() +}) + +export function useTenantAndProject() { + return { + currentOnlyId + } +} diff --git a/web-ui/src/layouts/LeftMode.vue b/web-ui/src/layouts/LeftMode.vue index 24551f3..3bc9764 100644 --- a/web-ui/src/layouts/LeftMode.vue +++ b/web-ui/src/layouts/LeftMode.vue @@ -3,13 +3,12 @@ import { computed } from "vue" import { storeToRefs } from "pinia" import { useAppStore } from "@/store/modules/app" import { useSettingsStore } from "@/store/modules/settings" -import { useTenantStoreHook } from "@/store/modules/tenant" +import { useTenantAndProject } from "@/hooks/useTenantAndProject" import { AppMain, NavigationBar, Sidebar, TagsView } from "./components" import { DeviceEnum } from "@/constants/app-key" const appStore = useAppStore() const settingsStore = useSettingsStore() -const tenantStore = useTenantStoreHook() const { showTagsView, fixedHeader } = storeToRefs(settingsStore) @@ -28,9 +27,7 @@ const handleClickOutside = () => { appStore.closeSidebar(false) } -const currentTenantId = computed(() => { - return tenantStore.getCurrentTenantId() -}) +const { currentOnlyId } = useTenantAndProject() diff --git a/web-ui/src/layouts/LeftTopMode.vue b/web-ui/src/layouts/LeftTopMode.vue index 5faaad3..dcb4096 100644 --- a/web-ui/src/layouts/LeftTopMode.vue +++ b/web-ui/src/layouts/LeftTopMode.vue @@ -2,13 +2,12 @@ import { computed } from "vue" import { storeToRefs } from "pinia" import { useAppStore } from "@/store/modules/app" -import { useTenantStoreHook } from "@/store/modules/tenant" +import { useTenantAndProject } from "@/hooks/useTenantAndProject" import { useSettingsStore } from "@/store/modules/settings" import { AppMain, NavigationBar, Sidebar, TagsView, Logo } from "./components" const appStore = useAppStore() const settingsStore = useSettingsStore() -const tenantStore = useTenantStoreHook() const { showTagsView, showLogo } = storeToRefs(settingsStore) @@ -19,9 +18,7 @@ const layoutClasses = computed(() => { } }) -const currentTenantId = computed(() => { - return tenantStore.getCurrentTenantId() -}) +const { currentOnlyId } = useTenantAndProject() diff --git a/web-ui/src/layouts/TopMode.vue b/web-ui/src/layouts/TopMode.vue index 75736fc..983b049 100644 --- a/web-ui/src/layouts/TopMode.vue +++ b/web-ui/src/layouts/TopMode.vue @@ -1,18 +1,12 @@ diff --git a/web-ui/src/layouts/components/SearchTenantAndTenant/index.vue b/web-ui/src/layouts/components/SearchTenantAndTenant/index.vue index b171c22..2b593e5 100644 --- a/web-ui/src/layouts/components/SearchTenantAndTenant/index.vue +++ b/web-ui/src/layouts/components/SearchTenantAndTenant/index.vue @@ -9,12 +9,12 @@ class="search-modal__private" append-to-body > - + - + - + @@ -22,13 +22,13 @@ @@ -76,9 +80,11 @@ const tabsHandleClick = (tab: TabsPaneContext) => { .svg-icon { font-size: 18px; } + .el-dialog__header { display: none; } + .el-dialog__footer { border-top: 1px solid var(--el-border-color); padding: var(--el-dialog-padding-primary); diff --git a/web-ui/src/types/cms.ts b/web-ui/src/types/cms.ts index c69a5d2..02687ee 100644 --- a/web-ui/src/types/cms.ts +++ b/web-ui/src/types/cms.ts @@ -1,7 +1,8 @@ +import { IBaseEntity } from "./base" import { IPmsDept } from "./pms" /** 项目 */ -export interface ICmsProject { +export interface ICmsProject extends IBaseEntity { /* 项目ID */ projectId: number /*项目名称*/ @@ -25,3 +26,13 @@ export interface ICmsProject { /*部门*/ dept: IPmsDept | null } + +/** 项目 */ +export interface ICmsDevice extends IBaseEntity { + /* 设备ID */ + deviceId: number + /*设备名称*/ + deviceName: string + /*状态 【0.禁用 1.正常 2.锁定 】*/ + status: number +} diff --git a/web-ui/src/utils/cache/local-storage.ts b/web-ui/src/utils/cache/local-storage.ts index 2b48e67..43399a1 100644 --- a/web-ui/src/utils/cache/local-storage.ts +++ b/web-ui/src/utils/cache/local-storage.ts @@ -5,8 +5,8 @@ import { type SidebarOpened, type SidebarClosed } from "@/constants/app-key" import { type ThemeName } from "@/hooks/useTheme" import { type TagView } from "@/store/modules/tags-view" import { type LayoutSettings } from "@/config/layouts" -import { ISimpleProject, type ISimpleTenant } from "@/types/base" -import { defaultProject, defaultTenant } from "@/utils" +import { type ISimpleTenant } from "@/types/base" +import { defaultTenant } from "@/utils" //#region 系统布局配置 export const getConfigLayout = () => { diff --git a/web-ui/src/utils/service.ts b/web-ui/src/utils/service.ts index 6c99ed1..ffee8c4 100644 --- a/web-ui/src/utils/service.ts +++ b/web-ui/src/utils/service.ts @@ -4,6 +4,7 @@ import { ElMessage, ElMessageBox } from "element-plus" import { get, merge } from "lodash-es" import { getToken } from "./cache/cookies" import { useTenantStoreHook } from "@/store/modules/tenant" +import { useProjectStoreHook } from "@/store/modules/project" import { getEnvBaseURLPrefix } from "." import { ISimpleTenant } from "@/types/base" import { defaultTenant } from "@/utils" @@ -137,15 +138,15 @@ function logout(message: string) { function createRequest(service: AxiosInstance) { return function (config: AxiosRequestConfig): Promise { const tokenValue = getToken() - let tenant: ISimpleTenant = useTenantStoreHook().getCurrentTenant() - if (config.url === "/auth/login") { - tenant = defaultTenant - } + const projectId = useProjectStoreHook().getCurrentProjectId() + // 如果是登录接口,就使用默认的 租户ID 进行登录 + const tenantId = config.url === "/auth/login" ? defaultTenant.tenantId : useTenantStoreHook().getCurrentTenantId() const defaultConfig = { headers: { // 携带 Token Authorization: tokenValue ? tokenValue : undefined, - "x-tenant-id": tenant.tenantId, + "x-tenant-id": tenantId, + "x-project-id": projectId, "Content-Type": "application/json" }, timeout: 10000, diff --git a/web-ui/src/views/device/list/index.vue b/web-ui/src/views/device/list/index.vue index 8249908..5e78dd6 100644 --- a/web-ui/src/views/device/list/index.vue +++ b/web-ui/src/views/device/list/index.vue @@ -1,12 +1,276 @@ diff --git a/web-ui/src/views/project/list/index.vue b/web-ui/src/views/project/list/index.vue index a1ba866..68c0e12 100644 --- a/web-ui/src/views/project/list/index.vue +++ b/web-ui/src/views/project/list/index.vue @@ -75,7 +75,7 @@
重置密码
-
+
删除
diff --git a/web-ui/src/views/sys/tenant/create.vue b/web-ui/src/views/sys/tenant/create.vue index e5e1301..a886d1b 100644 --- a/web-ui/src/views/sys/tenant/create.vue +++ b/web-ui/src/views/sys/tenant/create.vue @@ -249,7 +249,7 @@ const adminResult = ref({ const entityFormRef = ref(null) const tenantIdValidator = (rule: any, value: any, callback: any) => { - if (!/\w{4,12}$/.test(value)) { + if (!/^\w{4,12}$/.test(value)) { callback(new Error("只能是字母和数字,长度4~12位!")) } else { callback() diff --git a/web-ui/src/views/sys/tenant/index.vue b/web-ui/src/views/sys/tenant/index.vue index ae1d92f..970ad4d 100644 --- a/web-ui/src/views/sys/tenant/index.vue +++ b/web-ui/src/views/sys/tenant/index.vue @@ -11,7 +11,9 @@ 新增 -
+ +
+ @@ -459,27 +461,36 @@ const handleAdd = () => { // 删除 const handleDelete = (data: ISysTenant) => { - ElMessageBox.confirm(`确要删除 ${data.name} 吗?`, "警告", { + ElMessageBox.prompt(`请在下方的输入框中填写租户ID`, `确要删除 ${data.name} 吗?`, { confirmButtonText: "确定", cancelButtonText: "取消", + inputPattern: /^\w{4,12}$/, + inputErrorMessage: "租户ID可以是字母和数字,长度4~12位", type: "warning" }) - .then(() => { - deleteTenantApi(data.tenantId) - .then((resp) => { - if (resp.data) { - ElMessage({ - message: `删除 ${data.name} 成功!`, - type: "success" - }) - } - }) - .catch((err) => { - console.log("err :>> ", err) - }) - .finally(() => { - getTableData() + .then((value) => { + if (value.value === data.tenantId) { + deleteTenantApi(data.tenantId) + .then((resp) => { + if (resp.data) { + ElMessage({ + message: `删除 ${data.name} 成功!`, + type: "success" + }) + } + }) + .catch((err) => { + console.log("err :>> ", err) + }) + .finally(() => { + getTableData() + }) + } else { + ElMessage({ + message: `租户ID输入错误!`, + type: "error" }) + } }) .catch((error) => { console.log("error :>> ", error)