feat: 搭建后端多租户名片服务
- 初始化 Spring Boot 多模块工程与通用基础能力 - 增加租户、组织、用户、名片、文件、统计等业务模块 - 补充数据库迁移脚本与基础测试
This commit is contained in:
50
backend/easycard-module-tenant/pom.xml
Normal file
50
backend/easycard-module-tenant/pom.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.easycard</groupId>
|
||||
<artifactId>easycard-parent</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>easycard-module-tenant</artifactId>
|
||||
<name>easycard-module-tenant</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.easycard</groupId>
|
||||
<artifactId>easycard-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.easycard</groupId>
|
||||
<artifactId>easycard-module-user</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.easycard</groupId>
|
||||
<artifactId>easycard-module-card</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.easycard</groupId>
|
||||
<artifactId>easycard-module-file</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.easycard</groupId>
|
||||
<artifactId>easycard-module-stat</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.easycard.module.tenant.controller;
|
||||
|
||||
import com.easycard.common.api.ApiResponse;
|
||||
import com.easycard.module.tenant.service.PlatformTenantService;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/platform/miniapps")
|
||||
public class PlatformMiniappController {
|
||||
|
||||
private final PlatformTenantService platformTenantService;
|
||||
|
||||
public PlatformMiniappController(PlatformTenantService platformTenantService) {
|
||||
this.platformTenantService = platformTenantService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAuthority('PLATFORM_SUPER_ADMIN')")
|
||||
public ApiResponse<List<PlatformTenantService.MiniappConfigView>> listMiniapps() {
|
||||
return ApiResponse.success(platformTenantService.listMiniappConfigs());
|
||||
}
|
||||
|
||||
@PutMapping("/{tenantId}")
|
||||
@PreAuthorize("hasAuthority('PLATFORM_SUPER_ADMIN')")
|
||||
public ApiResponse<PlatformTenantService.MiniappConfigView> updateMiniapp(@PathVariable Long tenantId, @Valid @RequestBody MiniappConfigRequest request) {
|
||||
return ApiResponse.success(platformTenantService.updateMiniappConfig(tenantId, request.toCommand()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.easycard.module.tenant.controller;
|
||||
|
||||
import com.easycard.common.api.ApiResponse;
|
||||
import com.easycard.common.exception.BusinessException;
|
||||
import com.easycard.module.tenant.service.PlatformTenantService;
|
||||
import com.easycard.module.tenant.vo.TenantDetailResp;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/platform/tenants")
|
||||
public class PlatformTenantController {
|
||||
|
||||
private final PlatformTenantService platformTenantService;
|
||||
|
||||
public PlatformTenantController(PlatformTenantService platformTenantService) {
|
||||
this.platformTenantService = platformTenantService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAuthority('PLATFORM_SUPER_ADMIN')")
|
||||
public ApiResponse<List<TenantDetailResp>> listTenants() {
|
||||
return ApiResponse.success(platformTenantService.listTenants());
|
||||
}
|
||||
|
||||
@GetMapping("/{tenantId}")
|
||||
@PreAuthorize("hasAuthority('PLATFORM_SUPER_ADMIN')")
|
||||
public ApiResponse<TenantDetailResp> getTenant(@PathVariable Long tenantId) {
|
||||
TenantDetailResp tenant = platformTenantService.getTenantById(tenantId)
|
||||
.orElseThrow(() -> new BusinessException("TENANT_NOT_FOUND", "租户不存在"));
|
||||
return ApiResponse.success(tenant);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@PreAuthorize("hasAuthority('PLATFORM_SUPER_ADMIN')")
|
||||
public ApiResponse<TenantDetailResp> createTenant(@Valid @RequestBody CreateTenantRequest request) {
|
||||
return ApiResponse.success(platformTenantService.createTenant(request.toCommand()));
|
||||
}
|
||||
|
||||
@PutMapping("/{tenantId}")
|
||||
@PreAuthorize("hasAuthority('PLATFORM_SUPER_ADMIN')")
|
||||
public ApiResponse<TenantDetailResp> updateTenant(@PathVariable Long tenantId, @Valid @RequestBody UpdateTenantRequest request) {
|
||||
return ApiResponse.success(platformTenantService.updateTenant(tenantId, request.toCommand()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{tenantId}")
|
||||
@PreAuthorize("hasAuthority('PLATFORM_SUPER_ADMIN')")
|
||||
public ApiResponse<Void> deleteTenant(@PathVariable Long tenantId) {
|
||||
platformTenantService.deleteTenant(tenantId);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
}
|
||||
|
||||
record CreateTenantRequest(
|
||||
@NotBlank(message = "租户名称不能为空") String tenantName,
|
||||
@NotBlank(message = "管理员账号不能为空") String adminUsername,
|
||||
@NotBlank(message = "管理员密码不能为空") String adminPassword,
|
||||
String miniappAppId
|
||||
) {
|
||||
PlatformTenantService.CreateTenantCommand toCommand() {
|
||||
return new PlatformTenantService.CreateTenantCommand(
|
||||
null,
|
||||
tenantName,
|
||||
adminUsername,
|
||||
adminPassword,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
20,
|
||||
1024,
|
||||
null,
|
||||
new PlatformTenantService.MiniappConfigCommand(
|
||||
"PROD",
|
||||
miniappAppId,
|
||||
"",
|
||||
tenantName + "电子名片",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"v1.0.0",
|
||||
"DRAFT",
|
||||
null
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
record UpdateTenantRequest(
|
||||
@NotBlank(message = "租户名称不能为空") String tenantName,
|
||||
String miniappAppId
|
||||
) {
|
||||
PlatformTenantService.UpdateTenantCommand toCommand() {
|
||||
return new PlatformTenantService.UpdateTenantCommand(
|
||||
tenantName, miniappAppId, null, null, null, null, null, null, null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
record MiniappConfigRequest(
|
||||
String envCode,
|
||||
String miniappAppId,
|
||||
String miniappAppSecret,
|
||||
String miniappName,
|
||||
String miniappOriginalId,
|
||||
String requestDomain,
|
||||
String uploadDomain,
|
||||
String downloadDomain,
|
||||
String versionTag,
|
||||
String publishStatus,
|
||||
String remark
|
||||
) {
|
||||
PlatformTenantService.MiniappConfigCommand toCommand() {
|
||||
return new PlatformTenantService.MiniappConfigCommand(
|
||||
envCode == null ? "PROD" : envCode,
|
||||
miniappAppId,
|
||||
miniappAppSecret,
|
||||
miniappName,
|
||||
miniappOriginalId,
|
||||
requestDomain,
|
||||
uploadDomain,
|
||||
downloadDomain,
|
||||
versionTag,
|
||||
publishStatus == null ? "DRAFT" : publishStatus,
|
||||
remark
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.easycard.module.tenant.controller;
|
||||
|
||||
import com.easycard.common.api.ApiResponse;
|
||||
import com.easycard.module.tenant.service.PlatformTenantService;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/tenant/miniapp-config")
|
||||
public class TenantMiniappController {
|
||||
|
||||
private final PlatformTenantService platformTenantService;
|
||||
|
||||
public TenantMiniappController(PlatformTenantService platformTenantService) {
|
||||
this.platformTenantService = platformTenantService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
public ApiResponse<PlatformTenantService.MiniappConfigView> getCurrentMiniappConfig() {
|
||||
return ApiResponse.success(platformTenantService.getCurrentTenantMiniappConfig());
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
public ApiResponse<PlatformTenantService.MiniappConfigView> updateCurrentMiniappConfig(@RequestBody UpdateTenantMiniappConfigRequest request) {
|
||||
return ApiResponse.success(platformTenantService.updateCurrentTenantMiniappConfig(request == null ? null : request.miniappAppId()));
|
||||
}
|
||||
}
|
||||
|
||||
record UpdateTenantMiniappConfigRequest(String miniappAppId) {
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package com.easycard.module.tenant.dal.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@TableName("sys_tenant")
|
||||
public class TenantDO {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private String tenantCode;
|
||||
private String tenantName;
|
||||
private String tenantShortName;
|
||||
private String contactName;
|
||||
private String contactPhone;
|
||||
private String contactEmail;
|
||||
private String tenantStatus;
|
||||
private LocalDateTime expireAt;
|
||||
private Integer userLimit;
|
||||
private Integer storageLimitMb;
|
||||
private String remark;
|
||||
private Long createdBy;
|
||||
private LocalDateTime createdTime;
|
||||
private Long updatedBy;
|
||||
private LocalDateTime updatedTime;
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTenantCode() {
|
||||
return tenantCode;
|
||||
}
|
||||
|
||||
public void setTenantCode(String tenantCode) {
|
||||
this.tenantCode = tenantCode;
|
||||
}
|
||||
|
||||
public String getTenantName() {
|
||||
return tenantName;
|
||||
}
|
||||
|
||||
public void setTenantName(String tenantName) {
|
||||
this.tenantName = tenantName;
|
||||
}
|
||||
|
||||
public String getTenantShortName() {
|
||||
return tenantShortName;
|
||||
}
|
||||
|
||||
public void setTenantShortName(String tenantShortName) {
|
||||
this.tenantShortName = tenantShortName;
|
||||
}
|
||||
|
||||
public String getContactName() {
|
||||
return contactName;
|
||||
}
|
||||
|
||||
public void setContactName(String contactName) {
|
||||
this.contactName = contactName;
|
||||
}
|
||||
|
||||
public String getContactPhone() {
|
||||
return contactPhone;
|
||||
}
|
||||
|
||||
public void setContactPhone(String contactPhone) {
|
||||
this.contactPhone = contactPhone;
|
||||
}
|
||||
|
||||
public String getContactEmail() {
|
||||
return contactEmail;
|
||||
}
|
||||
|
||||
public void setContactEmail(String contactEmail) {
|
||||
this.contactEmail = contactEmail;
|
||||
}
|
||||
|
||||
public String getTenantStatus() {
|
||||
return tenantStatus;
|
||||
}
|
||||
|
||||
public void setTenantStatus(String tenantStatus) {
|
||||
this.tenantStatus = tenantStatus;
|
||||
}
|
||||
|
||||
public LocalDateTime getExpireAt() {
|
||||
return expireAt;
|
||||
}
|
||||
|
||||
public void setExpireAt(LocalDateTime expireAt) {
|
||||
this.expireAt = expireAt;
|
||||
}
|
||||
|
||||
public Integer getUserLimit() {
|
||||
return userLimit;
|
||||
}
|
||||
|
||||
public void setUserLimit(Integer userLimit) {
|
||||
this.userLimit = userLimit;
|
||||
}
|
||||
|
||||
public Integer getStorageLimitMb() {
|
||||
return storageLimitMb;
|
||||
}
|
||||
|
||||
public void setStorageLimitMb(Integer storageLimitMb) {
|
||||
this.storageLimitMb = storageLimitMb;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Long getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
public void setCreatedBy(Long createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedTime() {
|
||||
return createdTime;
|
||||
}
|
||||
|
||||
public void setCreatedTime(LocalDateTime createdTime) {
|
||||
this.createdTime = createdTime;
|
||||
}
|
||||
|
||||
public Long getUpdatedBy() {
|
||||
return updatedBy;
|
||||
}
|
||||
|
||||
public void setUpdatedBy(Long updatedBy) {
|
||||
this.updatedBy = updatedBy;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedTime() {
|
||||
return updatedTime;
|
||||
}
|
||||
|
||||
public void setUpdatedTime(LocalDateTime updatedTime) {
|
||||
this.updatedTime = updatedTime;
|
||||
}
|
||||
|
||||
public Integer getDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public void setDeleted(Integer deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package com.easycard.module.tenant.dal.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@TableName("tenant_miniapp_config")
|
||||
public class TenantMiniappConfigDO {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Long tenantId;
|
||||
private String envCode;
|
||||
private String miniappAppId;
|
||||
private String miniappAppSecret;
|
||||
private String miniappName;
|
||||
private String miniappOriginalId;
|
||||
private String requestDomain;
|
||||
private String uploadDomain;
|
||||
private String downloadDomain;
|
||||
private String versionTag;
|
||||
private String publishStatus;
|
||||
private LocalDateTime lastPublishedAt;
|
||||
private String remark;
|
||||
private Long createdBy;
|
||||
private LocalDateTime createdTime;
|
||||
private Long updatedBy;
|
||||
private LocalDateTime updatedTime;
|
||||
@TableLogic
|
||||
private Integer deleted;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
public String getEnvCode() {
|
||||
return envCode;
|
||||
}
|
||||
|
||||
public void setEnvCode(String envCode) {
|
||||
this.envCode = envCode;
|
||||
}
|
||||
|
||||
public String getMiniappAppId() {
|
||||
return miniappAppId;
|
||||
}
|
||||
|
||||
public void setMiniappAppId(String miniappAppId) {
|
||||
this.miniappAppId = miniappAppId;
|
||||
}
|
||||
|
||||
public String getMiniappAppSecret() {
|
||||
return miniappAppSecret;
|
||||
}
|
||||
|
||||
public void setMiniappAppSecret(String miniappAppSecret) {
|
||||
this.miniappAppSecret = miniappAppSecret;
|
||||
}
|
||||
|
||||
public String getMiniappName() {
|
||||
return miniappName;
|
||||
}
|
||||
|
||||
public void setMiniappName(String miniappName) {
|
||||
this.miniappName = miniappName;
|
||||
}
|
||||
|
||||
public String getMiniappOriginalId() {
|
||||
return miniappOriginalId;
|
||||
}
|
||||
|
||||
public void setMiniappOriginalId(String miniappOriginalId) {
|
||||
this.miniappOriginalId = miniappOriginalId;
|
||||
}
|
||||
|
||||
public String getRequestDomain() {
|
||||
return requestDomain;
|
||||
}
|
||||
|
||||
public void setRequestDomain(String requestDomain) {
|
||||
this.requestDomain = requestDomain;
|
||||
}
|
||||
|
||||
public String getUploadDomain() {
|
||||
return uploadDomain;
|
||||
}
|
||||
|
||||
public void setUploadDomain(String uploadDomain) {
|
||||
this.uploadDomain = uploadDomain;
|
||||
}
|
||||
|
||||
public String getDownloadDomain() {
|
||||
return downloadDomain;
|
||||
}
|
||||
|
||||
public void setDownloadDomain(String downloadDomain) {
|
||||
this.downloadDomain = downloadDomain;
|
||||
}
|
||||
|
||||
public String getVersionTag() {
|
||||
return versionTag;
|
||||
}
|
||||
|
||||
public void setVersionTag(String versionTag) {
|
||||
this.versionTag = versionTag;
|
||||
}
|
||||
|
||||
public String getPublishStatus() {
|
||||
return publishStatus;
|
||||
}
|
||||
|
||||
public void setPublishStatus(String publishStatus) {
|
||||
this.publishStatus = publishStatus;
|
||||
}
|
||||
|
||||
public LocalDateTime getLastPublishedAt() {
|
||||
return lastPublishedAt;
|
||||
}
|
||||
|
||||
public void setLastPublishedAt(LocalDateTime lastPublishedAt) {
|
||||
this.lastPublishedAt = lastPublishedAt;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Long getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
public void setCreatedBy(Long createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedTime() {
|
||||
return createdTime;
|
||||
}
|
||||
|
||||
public void setCreatedTime(LocalDateTime createdTime) {
|
||||
this.createdTime = createdTime;
|
||||
}
|
||||
|
||||
public Long getUpdatedBy() {
|
||||
return updatedBy;
|
||||
}
|
||||
|
||||
public void setUpdatedBy(Long updatedBy) {
|
||||
this.updatedBy = updatedBy;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedTime() {
|
||||
return updatedTime;
|
||||
}
|
||||
|
||||
public void setUpdatedTime(LocalDateTime updatedTime) {
|
||||
this.updatedTime = updatedTime;
|
||||
}
|
||||
|
||||
public Integer getDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public void setDeleted(Integer deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.easycard.module.tenant.dal.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.easycard.module.tenant.dal.entity.TenantDO;
|
||||
|
||||
public interface TenantMapper extends BaseMapper<TenantDO> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.easycard.module.tenant.dal.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.easycard.module.tenant.dal.entity.TenantMiniappConfigDO;
|
||||
|
||||
public interface TenantMiniappConfigMapper extends BaseMapper<TenantMiniappConfigDO> {
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.easycard.module.tenant.service;
|
||||
|
||||
import com.easycard.common.tenant.TenantContext;
|
||||
import com.easycard.module.tenant.vo.TenantDetailResp;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface PlatformTenantService {
|
||||
|
||||
List<TenantDetailResp> listTenants();
|
||||
|
||||
Optional<TenantDetailResp> getTenantById(Long tenantId);
|
||||
|
||||
TenantDetailResp createTenant(CreateTenantCommand command);
|
||||
|
||||
TenantDetailResp updateTenant(Long tenantId, UpdateTenantCommand command);
|
||||
|
||||
void deleteTenant(Long tenantId);
|
||||
|
||||
List<MiniappConfigView> listMiniappConfigs();
|
||||
|
||||
MiniappConfigView updateMiniappConfig(Long tenantId, MiniappConfigCommand command);
|
||||
|
||||
MiniappConfigView getCurrentTenantMiniappConfig();
|
||||
|
||||
MiniappConfigView updateCurrentTenantMiniappConfig(String miniappAppId);
|
||||
|
||||
Optional<TenantContext> resolveTenantContextByMiniappAppId(String miniappAppId);
|
||||
|
||||
record CreateTenantCommand(
|
||||
String tenantCode,
|
||||
String tenantName,
|
||||
String adminUsername,
|
||||
String adminPassword,
|
||||
String tenantShortName,
|
||||
String contactName,
|
||||
String contactPhone,
|
||||
String contactEmail,
|
||||
Integer userLimit,
|
||||
Integer storageLimitMb,
|
||||
String remark,
|
||||
MiniappConfigCommand miniappConfig
|
||||
) {
|
||||
}
|
||||
|
||||
record UpdateTenantCommand(
|
||||
String tenantName,
|
||||
String miniappAppId,
|
||||
String tenantShortName,
|
||||
String contactName,
|
||||
String contactPhone,
|
||||
String contactEmail,
|
||||
Integer userLimit,
|
||||
Integer storageLimitMb,
|
||||
String remark
|
||||
) {
|
||||
}
|
||||
|
||||
record MiniappConfigCommand(
|
||||
String envCode,
|
||||
String miniappAppId,
|
||||
String miniappAppSecret,
|
||||
String miniappName,
|
||||
String miniappOriginalId,
|
||||
String requestDomain,
|
||||
String uploadDomain,
|
||||
String downloadDomain,
|
||||
String versionTag,
|
||||
String publishStatus,
|
||||
String remark
|
||||
) {
|
||||
}
|
||||
|
||||
record MiniappConfigView(
|
||||
Long tenantId,
|
||||
String tenantCode,
|
||||
String tenantName,
|
||||
String envCode,
|
||||
String miniappAppId,
|
||||
String miniappName,
|
||||
String miniappOriginalId,
|
||||
String requestDomain,
|
||||
String uploadDomain,
|
||||
String downloadDomain,
|
||||
String versionTag,
|
||||
String publishStatus
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,573 @@
|
||||
package com.easycard.module.tenant.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.easycard.common.auth.LoginUser;
|
||||
import com.easycard.common.auth.SecurityUtils;
|
||||
import com.easycard.common.exception.BusinessException;
|
||||
import com.easycard.common.tenant.TenantContext;
|
||||
import com.easycard.module.card.dal.entity.CardProfileDO;
|
||||
import com.easycard.module.card.dal.entity.CardProfileSpecialtyDO;
|
||||
import com.easycard.module.card.dal.mapper.CardProfileMapper;
|
||||
import com.easycard.module.card.dal.mapper.CardProfileSpecialtyMapper;
|
||||
import com.easycard.module.file.dal.entity.FileAssetDO;
|
||||
import com.easycard.module.file.dal.entity.FileAssetUsageDO;
|
||||
import com.easycard.module.file.dal.mapper.FileAssetMapper;
|
||||
import com.easycard.module.file.dal.mapper.FileAssetUsageMapper;
|
||||
import com.easycard.module.org.dal.entity.OrgDepartmentDO;
|
||||
import com.easycard.module.org.dal.entity.OrgFirmPracticeAreaDO;
|
||||
import com.easycard.module.org.dal.entity.OrgFirmProfileDO;
|
||||
import com.easycard.module.org.dal.mapper.OrgDepartmentMapper;
|
||||
import com.easycard.module.org.dal.mapper.OrgFirmPracticeAreaMapper;
|
||||
import com.easycard.module.org.dal.mapper.OrgFirmProfileMapper;
|
||||
import com.easycard.module.stat.dal.entity.CardShareLogDO;
|
||||
import com.easycard.module.stat.dal.entity.CardStatDailyDO;
|
||||
import com.easycard.module.stat.dal.entity.CardViewLogDO;
|
||||
import com.easycard.module.stat.dal.mapper.CardShareLogMapper;
|
||||
import com.easycard.module.stat.dal.mapper.CardStatDailyMapper;
|
||||
import com.easycard.module.stat.dal.mapper.CardViewLogMapper;
|
||||
import com.easycard.module.tenant.dal.entity.TenantDO;
|
||||
import com.easycard.module.tenant.dal.entity.TenantMiniappConfigDO;
|
||||
import com.easycard.module.tenant.dal.mapper.TenantMapper;
|
||||
import com.easycard.module.tenant.dal.mapper.TenantMiniappConfigMapper;
|
||||
import com.easycard.module.tenant.service.PlatformTenantService;
|
||||
import com.easycard.module.tenant.vo.TenantDetailResp;
|
||||
import com.easycard.module.user.dal.entity.SysLoginLogDO;
|
||||
import com.easycard.module.user.dal.entity.SysOperationLogDO;
|
||||
import com.easycard.module.user.dal.entity.SysRoleDO;
|
||||
import com.easycard.module.user.dal.entity.SysUserDO;
|
||||
import com.easycard.module.user.dal.entity.SysUserRoleDO;
|
||||
import com.easycard.module.user.dal.mapper.SysLoginLogMapper;
|
||||
import com.easycard.module.user.dal.mapper.SysOperationLogMapper;
|
||||
import com.easycard.module.user.dal.mapper.SysRoleMapper;
|
||||
import com.easycard.module.user.dal.mapper.SysUserMapper;
|
||||
import com.easycard.module.user.dal.mapper.SysUserRoleMapper;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
@Primary
|
||||
public class PlatformTenantServiceImpl implements PlatformTenantService {
|
||||
|
||||
private final TenantMapper tenantMapper;
|
||||
private final TenantMiniappConfigMapper tenantMiniappConfigMapper;
|
||||
private final SysUserMapper sysUserMapper;
|
||||
private final SysRoleMapper sysRoleMapper;
|
||||
private final SysUserRoleMapper sysUserRoleMapper;
|
||||
private final SysLoginLogMapper sysLoginLogMapper;
|
||||
private final SysOperationLogMapper sysOperationLogMapper;
|
||||
private final OrgDepartmentMapper orgDepartmentMapper;
|
||||
private final OrgFirmProfileMapper orgFirmProfileMapper;
|
||||
private final OrgFirmPracticeAreaMapper orgFirmPracticeAreaMapper;
|
||||
private final CardProfileMapper cardProfileMapper;
|
||||
private final CardProfileSpecialtyMapper cardProfileSpecialtyMapper;
|
||||
private final FileAssetMapper fileAssetMapper;
|
||||
private final FileAssetUsageMapper fileAssetUsageMapper;
|
||||
private final CardViewLogMapper cardViewLogMapper;
|
||||
private final CardShareLogMapper cardShareLogMapper;
|
||||
private final CardStatDailyMapper cardStatDailyMapper;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public PlatformTenantServiceImpl(
|
||||
TenantMapper tenantMapper,
|
||||
TenantMiniappConfigMapper tenantMiniappConfigMapper,
|
||||
SysUserMapper sysUserMapper,
|
||||
SysRoleMapper sysRoleMapper,
|
||||
SysUserRoleMapper sysUserRoleMapper,
|
||||
SysLoginLogMapper sysLoginLogMapper,
|
||||
SysOperationLogMapper sysOperationLogMapper,
|
||||
OrgDepartmentMapper orgDepartmentMapper,
|
||||
OrgFirmProfileMapper orgFirmProfileMapper,
|
||||
OrgFirmPracticeAreaMapper orgFirmPracticeAreaMapper,
|
||||
CardProfileMapper cardProfileMapper,
|
||||
CardProfileSpecialtyMapper cardProfileSpecialtyMapper,
|
||||
FileAssetMapper fileAssetMapper,
|
||||
FileAssetUsageMapper fileAssetUsageMapper,
|
||||
CardViewLogMapper cardViewLogMapper,
|
||||
CardShareLogMapper cardShareLogMapper,
|
||||
CardStatDailyMapper cardStatDailyMapper,
|
||||
PasswordEncoder passwordEncoder
|
||||
) {
|
||||
this.tenantMapper = tenantMapper;
|
||||
this.tenantMiniappConfigMapper = tenantMiniappConfigMapper;
|
||||
this.sysUserMapper = sysUserMapper;
|
||||
this.sysRoleMapper = sysRoleMapper;
|
||||
this.sysUserRoleMapper = sysUserRoleMapper;
|
||||
this.sysLoginLogMapper = sysLoginLogMapper;
|
||||
this.sysOperationLogMapper = sysOperationLogMapper;
|
||||
this.orgDepartmentMapper = orgDepartmentMapper;
|
||||
this.orgFirmProfileMapper = orgFirmProfileMapper;
|
||||
this.orgFirmPracticeAreaMapper = orgFirmPracticeAreaMapper;
|
||||
this.cardProfileMapper = cardProfileMapper;
|
||||
this.cardProfileSpecialtyMapper = cardProfileSpecialtyMapper;
|
||||
this.fileAssetMapper = fileAssetMapper;
|
||||
this.fileAssetUsageMapper = fileAssetUsageMapper;
|
||||
this.cardViewLogMapper = cardViewLogMapper;
|
||||
this.cardShareLogMapper = cardShareLogMapper;
|
||||
this.cardStatDailyMapper = cardStatDailyMapper;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TenantDetailResp> listTenants() {
|
||||
List<TenantDO> tenants = tenantMapper.selectList(
|
||||
Wrappers.<TenantDO>lambdaQuery().eq(TenantDO::getDeleted, 0).orderByDesc(TenantDO::getId)
|
||||
);
|
||||
if (tenants.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<Long> tenantIds = tenants.stream().map(TenantDO::getId).toList();
|
||||
List<TenantMiniappConfigDO> miniappConfigs = tenantMiniappConfigMapper.selectList(
|
||||
Wrappers.<TenantMiniappConfigDO>lambdaQuery()
|
||||
.in(TenantMiniappConfigDO::getTenantId, tenantIds)
|
||||
.eq(TenantMiniappConfigDO::getDeleted, 0)
|
||||
.eq(TenantMiniappConfigDO::getEnvCode, "PROD")
|
||||
);
|
||||
Map<Long, TenantMiniappConfigDO> miniappConfigMap = new HashMap<>();
|
||||
for (TenantMiniappConfigDO miniappConfig : miniappConfigs) {
|
||||
miniappConfigMap.put(miniappConfig.getTenantId(), miniappConfig);
|
||||
}
|
||||
|
||||
return tenants.stream()
|
||||
.map(tenant -> toResp(tenant, miniappConfigMap.get(tenant.getId())))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TenantDetailResp> getTenantById(Long tenantId) {
|
||||
TenantDO tenant = tenantMapper.selectById(tenantId);
|
||||
if (tenant == null || Integer.valueOf(1).equals(tenant.getDeleted())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
TenantMiniappConfigDO miniappConfig = tenantMiniappConfigMapper.selectOne(
|
||||
Wrappers.<TenantMiniappConfigDO>lambdaQuery()
|
||||
.eq(TenantMiniappConfigDO::getTenantId, tenantId)
|
||||
.eq(TenantMiniappConfigDO::getEnvCode, "PROD")
|
||||
.eq(TenantMiniappConfigDO::getDeleted, 0)
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
return Optional.of(toResp(tenant, miniappConfig));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public TenantDetailResp createTenant(CreateTenantCommand command) {
|
||||
String tenantCode = StringUtils.hasText(command.tenantCode())
|
||||
? command.tenantCode()
|
||||
: generateTenantCode(command.tenantName(), command.miniappConfig() == null ? null : command.miniappConfig().miniappAppId());
|
||||
validateTenantUniqueness(tenantCode, command.miniappConfig() == null ? null : command.miniappConfig().miniappAppId(), null);
|
||||
TenantDO tenant = new TenantDO();
|
||||
tenant.setTenantCode(tenantCode);
|
||||
tenant.setTenantName(command.tenantName());
|
||||
tenant.setTenantShortName(command.tenantShortName());
|
||||
tenant.setContactName(command.contactName());
|
||||
tenant.setContactPhone(command.contactPhone());
|
||||
tenant.setContactEmail(command.contactEmail());
|
||||
tenant.setTenantStatus("ENABLED");
|
||||
tenant.setExpireAt(null);
|
||||
tenant.setUserLimit(command.userLimit());
|
||||
tenant.setStorageLimitMb(command.storageLimitMb());
|
||||
tenant.setRemark(command.remark());
|
||||
tenant.setCreatedBy(1L);
|
||||
tenant.setUpdatedBy(1L);
|
||||
tenant.setDeleted(0);
|
||||
tenantMapper.insert(tenant);
|
||||
|
||||
createBuiltinRoles(tenant.getId());
|
||||
createTenantAdminUser(tenant.getId(), command.adminUsername(), command.adminPassword(), command.tenantName());
|
||||
TenantMiniappConfigDO miniappConfig = saveMiniappConfigInternal(tenant.getId(), command.miniappConfig(), null, 1L);
|
||||
return toResp(tenant, miniappConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public TenantDetailResp updateTenant(Long tenantId, UpdateTenantCommand command) {
|
||||
TenantDO tenant = getRequiredTenant(tenantId);
|
||||
String miniappAppId = StringUtils.hasText(command.miniappAppId()) ? command.miniappAppId().trim() : null;
|
||||
validateTenantUniqueness(null, miniappAppId, tenantId);
|
||||
tenant.setTenantName(command.tenantName());
|
||||
tenant.setTenantShortName(command.tenantShortName());
|
||||
tenant.setContactName(command.contactName());
|
||||
tenant.setContactPhone(command.contactPhone());
|
||||
tenant.setContactEmail(command.contactEmail());
|
||||
tenant.setUserLimit(command.userLimit());
|
||||
tenant.setStorageLimitMb(command.storageLimitMb());
|
||||
tenant.setRemark(command.remark());
|
||||
tenant.setUpdatedBy(1L);
|
||||
tenantMapper.updateById(tenant);
|
||||
TenantMiniappConfigDO miniappConfig = tenantMiniappConfigMapper.selectOne(Wrappers.<TenantMiniappConfigDO>lambdaQuery()
|
||||
.eq(TenantMiniappConfigDO::getTenantId, tenantId)
|
||||
.eq(TenantMiniappConfigDO::getEnvCode, "PROD")
|
||||
.eq(TenantMiniappConfigDO::getDeleted, 0)
|
||||
.last("LIMIT 1"));
|
||||
if (miniappConfig != null || miniappAppId != null) {
|
||||
miniappConfig = saveMiniappConfigInternal(
|
||||
tenantId,
|
||||
new MiniappConfigCommand(
|
||||
miniappConfig == null ? "PROD" : miniappConfig.getEnvCode(),
|
||||
miniappAppId,
|
||||
miniappConfig == null ? null : miniappConfig.getMiniappAppSecret(),
|
||||
miniappConfig == null ? tenant.getTenantName() + "电子名片" : miniappConfig.getMiniappName(),
|
||||
miniappConfig == null ? null : miniappConfig.getMiniappOriginalId(),
|
||||
miniappConfig == null ? null : miniappConfig.getRequestDomain(),
|
||||
miniappConfig == null ? null : miniappConfig.getUploadDomain(),
|
||||
miniappConfig == null ? null : miniappConfig.getDownloadDomain(),
|
||||
miniappConfig == null ? "v1.0.0" : miniappConfig.getVersionTag(),
|
||||
miniappConfig == null ? "DRAFT" : miniappConfig.getPublishStatus(),
|
||||
miniappConfig == null ? null : miniappConfig.getRemark()
|
||||
),
|
||||
miniappConfig,
|
||||
1L
|
||||
);
|
||||
}
|
||||
return toResp(tenant, miniappConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteTenant(Long tenantId) {
|
||||
TenantDO tenant = getRequiredTenant(tenantId);
|
||||
|
||||
sysUserRoleMapper.delete(Wrappers.<SysUserRoleDO>lambdaQuery().eq(SysUserRoleDO::getTenantId, tenantId));
|
||||
sysLoginLogMapper.delete(Wrappers.<SysLoginLogDO>lambdaQuery().eq(SysLoginLogDO::getTenantId, tenantId));
|
||||
sysOperationLogMapper.delete(Wrappers.<SysOperationLogDO>lambdaQuery().eq(SysOperationLogDO::getTenantId, tenantId));
|
||||
cardViewLogMapper.delete(Wrappers.<CardViewLogDO>lambdaQuery().eq(CardViewLogDO::getTenantId, tenantId));
|
||||
cardShareLogMapper.delete(Wrappers.<CardShareLogDO>lambdaQuery().eq(CardShareLogDO::getTenantId, tenantId));
|
||||
cardStatDailyMapper.delete(Wrappers.<CardStatDailyDO>lambdaQuery().eq(CardStatDailyDO::getTenantId, tenantId));
|
||||
|
||||
cardProfileSpecialtyMapper.deleteForceByTenantId(tenantId);
|
||||
cardProfileMapper.delete(Wrappers.<CardProfileDO>lambdaQuery().eq(CardProfileDO::getTenantId, tenantId));
|
||||
fileAssetUsageMapper.delete(Wrappers.<FileAssetUsageDO>lambdaQuery().eq(FileAssetUsageDO::getTenantId, tenantId));
|
||||
fileAssetMapper.delete(Wrappers.<FileAssetDO>lambdaQuery().eq(FileAssetDO::getTenantId, tenantId));
|
||||
orgDepartmentMapper.delete(Wrappers.<OrgDepartmentDO>lambdaQuery().eq(OrgDepartmentDO::getTenantId, tenantId));
|
||||
orgFirmPracticeAreaMapper.delete(Wrappers.<OrgFirmPracticeAreaDO>lambdaQuery().eq(OrgFirmPracticeAreaDO::getTenantId, tenantId));
|
||||
orgFirmProfileMapper.delete(Wrappers.<OrgFirmProfileDO>lambdaQuery().eq(OrgFirmProfileDO::getTenantId, tenantId));
|
||||
sysUserMapper.delete(Wrappers.<SysUserDO>lambdaQuery().eq(SysUserDO::getTenantId, tenantId));
|
||||
sysRoleMapper.delete(Wrappers.<SysRoleDO>lambdaQuery().eq(SysRoleDO::getTenantId, tenantId));
|
||||
tenantMiniappConfigMapper.delete(Wrappers.<TenantMiniappConfigDO>lambdaQuery().eq(TenantMiniappConfigDO::getTenantId, tenantId));
|
||||
tenantMapper.deleteById(tenant.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MiniappConfigView> listMiniappConfigs() {
|
||||
List<TenantMiniappConfigDO> configs = tenantMiniappConfigMapper.selectList(Wrappers.<TenantMiniappConfigDO>lambdaQuery()
|
||||
.eq(TenantMiniappConfigDO::getDeleted, 0)
|
||||
.orderByDesc(TenantMiniappConfigDO::getId));
|
||||
if (configs.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<Long> tenantIds = configs.stream().map(TenantMiniappConfigDO::getTenantId).distinct().toList();
|
||||
Map<Long, TenantDO> tenantMap = tenantMapper.selectBatchIds(tenantIds).stream()
|
||||
.collect(HashMap::new, (map, item) -> map.put(item.getId(), item), HashMap::putAll);
|
||||
return configs.stream().map(config -> {
|
||||
TenantDO tenant = tenantMap.get(config.getTenantId());
|
||||
return new MiniappConfigView(
|
||||
config.getTenantId(),
|
||||
tenant == null ? "" : tenant.getTenantCode(),
|
||||
tenant == null ? "" : tenant.getTenantName(),
|
||||
config.getEnvCode(),
|
||||
config.getMiniappAppId(),
|
||||
config.getMiniappName(),
|
||||
config.getMiniappOriginalId(),
|
||||
config.getRequestDomain(),
|
||||
config.getUploadDomain(),
|
||||
config.getDownloadDomain(),
|
||||
config.getVersionTag(),
|
||||
config.getPublishStatus()
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public MiniappConfigView updateMiniappConfig(Long tenantId, MiniappConfigCommand command) {
|
||||
TenantDO tenant = getRequiredTenant(tenantId);
|
||||
validateTenantUniqueness(null, command.miniappAppId(), tenantId);
|
||||
TenantMiniappConfigDO config = saveMiniappConfigInternal(tenantId, command, getTenantMiniappConfig(tenantId, command.envCode()), 1L);
|
||||
return toMiniappConfigView(tenant, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MiniappConfigView getCurrentTenantMiniappConfig() {
|
||||
LoginUser loginUser = SecurityUtils.getRequiredLoginUser();
|
||||
Long tenantId = getRequiredCurrentTenantId(loginUser);
|
||||
TenantDO tenant = getRequiredTenant(tenantId);
|
||||
return toMiniappConfigView(tenant, getTenantMiniappConfig(tenantId, "PROD"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public MiniappConfigView updateCurrentTenantMiniappConfig(String miniappAppId) {
|
||||
LoginUser loginUser = SecurityUtils.getRequiredLoginUser();
|
||||
Long tenantId = getRequiredCurrentTenantId(loginUser);
|
||||
TenantDO tenant = getRequiredTenant(tenantId);
|
||||
String normalizedAppId = StringUtils.hasText(miniappAppId) ? miniappAppId.trim() : null;
|
||||
validateTenantUniqueness(null, normalizedAppId, tenantId);
|
||||
|
||||
TenantMiniappConfigDO existed = getTenantMiniappConfig(tenantId, "PROD");
|
||||
TenantMiniappConfigDO saved = saveMiniappConfigInternal(
|
||||
tenantId,
|
||||
new MiniappConfigCommand(
|
||||
"PROD",
|
||||
normalizedAppId,
|
||||
existed == null ? null : existed.getMiniappAppSecret(),
|
||||
existed == null ? tenant.getTenantName() + "电子名片" : existed.getMiniappName(),
|
||||
existed == null ? null : existed.getMiniappOriginalId(),
|
||||
existed == null ? null : existed.getRequestDomain(),
|
||||
existed == null ? null : existed.getUploadDomain(),
|
||||
existed == null ? null : existed.getDownloadDomain(),
|
||||
existed == null ? "v1.0.0" : existed.getVersionTag(),
|
||||
existed == null ? "DRAFT" : existed.getPublishStatus(),
|
||||
existed == null ? null : existed.getRemark()
|
||||
),
|
||||
existed,
|
||||
loginUser.userId()
|
||||
);
|
||||
return toMiniappConfigView(tenant, saved);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TenantContext> resolveTenantContextByMiniappAppId(String miniappAppId) {
|
||||
if (!StringUtils.hasText(miniappAppId)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
TenantMiniappConfigDO miniappConfig = tenantMiniappConfigMapper.selectOne(
|
||||
Wrappers.<TenantMiniappConfigDO>lambdaQuery()
|
||||
.eq(TenantMiniappConfigDO::getMiniappAppId, miniappAppId)
|
||||
.eq(TenantMiniappConfigDO::getDeleted, 0)
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
if (miniappConfig == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
TenantDO tenant = tenantMapper.selectById(miniappConfig.getTenantId());
|
||||
if (tenant == null || Integer.valueOf(1).equals(tenant.getDeleted())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(new TenantContext(tenant.getId(), tenant.getTenantCode(), miniappAppId));
|
||||
}
|
||||
|
||||
private void validateTenantUniqueness(String tenantCode, String miniappAppId, Long currentTenantId) {
|
||||
if (StringUtils.hasText(tenantCode)) {
|
||||
TenantDO existedTenant = tenantMapper.selectOne(Wrappers.<TenantDO>lambdaQuery()
|
||||
.eq(TenantDO::getTenantCode, tenantCode)
|
||||
.eq(TenantDO::getDeleted, 0)
|
||||
.last("LIMIT 1"));
|
||||
if (existedTenant != null && !existedTenant.getId().equals(currentTenantId)) {
|
||||
throw new BusinessException("TENANT_CODE_DUPLICATED", "租户编码已存在");
|
||||
}
|
||||
}
|
||||
if (StringUtils.hasText(miniappAppId)) {
|
||||
TenantMiniappConfigDO existedConfig = tenantMiniappConfigMapper.selectOne(Wrappers.<TenantMiniappConfigDO>lambdaQuery()
|
||||
.eq(TenantMiniappConfigDO::getMiniappAppId, miniappAppId)
|
||||
.eq(TenantMiniappConfigDO::getDeleted, 0)
|
||||
.last("LIMIT 1"));
|
||||
if (existedConfig != null && !existedConfig.getTenantId().equals(currentTenantId)) {
|
||||
throw new BusinessException("MINIAPP_APP_ID_DUPLICATED", "小程序 AppID 已存在");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TenantDO getRequiredTenant(Long tenantId) {
|
||||
TenantDO tenant = tenantMapper.selectById(tenantId);
|
||||
if (tenant == null || Integer.valueOf(1).equals(tenant.getDeleted())) {
|
||||
throw new BusinessException("TENANT_NOT_FOUND", "租户不存在");
|
||||
}
|
||||
return tenant;
|
||||
}
|
||||
|
||||
private Long getRequiredCurrentTenantId(LoginUser loginUser) {
|
||||
if (loginUser.tenantId() == null) {
|
||||
throw new BusinessException("TENANT_CONTEXT_MISSING", "当前账号未绑定租户");
|
||||
}
|
||||
return loginUser.tenantId();
|
||||
}
|
||||
|
||||
private TenantMiniappConfigDO getTenantMiniappConfig(Long tenantId, String envCode) {
|
||||
return tenantMiniappConfigMapper.selectOne(Wrappers.<TenantMiniappConfigDO>lambdaQuery()
|
||||
.eq(TenantMiniappConfigDO::getTenantId, tenantId)
|
||||
.eq(TenantMiniappConfigDO::getEnvCode, envCode)
|
||||
.eq(TenantMiniappConfigDO::getDeleted, 0)
|
||||
.last("LIMIT 1"));
|
||||
}
|
||||
|
||||
private TenantMiniappConfigDO saveMiniappConfigInternal(Long tenantId, MiniappConfigCommand command, TenantMiniappConfigDO config, Long operatorId) {
|
||||
if (command == null) {
|
||||
return null;
|
||||
}
|
||||
if (config == null) {
|
||||
config = new TenantMiniappConfigDO();
|
||||
config.setTenantId(tenantId);
|
||||
config.setCreatedBy(operatorId);
|
||||
config.setDeleted(0);
|
||||
}
|
||||
config.setEnvCode(StringUtils.hasText(command.envCode()) ? command.envCode() : "PROD");
|
||||
config.setMiniappAppId(StringUtils.hasText(command.miniappAppId()) ? command.miniappAppId().trim() : null);
|
||||
config.setMiniappAppSecret(command.miniappAppSecret());
|
||||
config.setMiniappName(command.miniappName());
|
||||
config.setMiniappOriginalId(command.miniappOriginalId());
|
||||
config.setRequestDomain(command.requestDomain());
|
||||
config.setUploadDomain(command.uploadDomain());
|
||||
config.setDownloadDomain(command.downloadDomain());
|
||||
config.setVersionTag(command.versionTag());
|
||||
config.setPublishStatus(command.publishStatus());
|
||||
config.setRemark(command.remark());
|
||||
config.setUpdatedBy(operatorId);
|
||||
if ("PUBLISHED".equals(command.publishStatus())) {
|
||||
config.setLastPublishedAt(LocalDateTime.now());
|
||||
}
|
||||
if (config.getId() == null) {
|
||||
tenantMiniappConfigMapper.insert(config);
|
||||
} else {
|
||||
tenantMiniappConfigMapper.updateById(config);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
private MiniappConfigView toMiniappConfigView(TenantDO tenant, TenantMiniappConfigDO config) {
|
||||
if (config == null) {
|
||||
return new MiniappConfigView(
|
||||
tenant.getId(),
|
||||
emptyIfNull(tenant.getTenantCode()),
|
||||
emptyIfNull(tenant.getTenantName()),
|
||||
"PROD",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"v1.0.0",
|
||||
"UNCONFIGURED"
|
||||
);
|
||||
}
|
||||
return new MiniappConfigView(
|
||||
tenant.getId(),
|
||||
emptyIfNull(tenant.getTenantCode()),
|
||||
emptyIfNull(tenant.getTenantName()),
|
||||
StringUtils.hasText(config.getEnvCode()) ? config.getEnvCode() : "PROD",
|
||||
emptyIfNull(config.getMiniappAppId()),
|
||||
emptyIfNull(config.getMiniappName()),
|
||||
emptyIfNull(config.getMiniappOriginalId()),
|
||||
emptyIfNull(config.getRequestDomain()),
|
||||
emptyIfNull(config.getUploadDomain()),
|
||||
emptyIfNull(config.getDownloadDomain()),
|
||||
StringUtils.hasText(config.getVersionTag()) ? config.getVersionTag() : "v1.0.0",
|
||||
StringUtils.hasText(config.getPublishStatus()) ? config.getPublishStatus() : "DRAFT"
|
||||
);
|
||||
}
|
||||
|
||||
private String emptyIfNull(String value) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
|
||||
private void createBuiltinRoles(Long tenantId) {
|
||||
insertRoleIfAbsent(tenantId, "TENANT_ADMIN", "租户管理员", "TENANT");
|
||||
insertRoleIfAbsent(tenantId, "TENANT_USER", "普通用户", "SELF");
|
||||
}
|
||||
|
||||
private void insertRoleIfAbsent(Long tenantId, String roleCode, String roleName, String dataScope) {
|
||||
SysRoleDO existed = sysRoleMapper.selectOne(Wrappers.<SysRoleDO>lambdaQuery()
|
||||
.eq(SysRoleDO::getTenantId, tenantId)
|
||||
.eq(SysRoleDO::getRoleCode, roleCode)
|
||||
.eq(SysRoleDO::getDeleted, 0)
|
||||
.last("LIMIT 1"));
|
||||
if (existed != null) {
|
||||
return;
|
||||
}
|
||||
SysRoleDO role = new SysRoleDO();
|
||||
role.setTenantId(tenantId);
|
||||
role.setRoleScope("TENANT");
|
||||
role.setRoleCode(roleCode);
|
||||
role.setRoleName(roleName);
|
||||
role.setDataScope(dataScope);
|
||||
role.setIsBuiltin(1);
|
||||
role.setRoleStatus("ENABLED");
|
||||
role.setCreatedBy(1L);
|
||||
role.setUpdatedBy(1L);
|
||||
role.setDeleted(0);
|
||||
sysRoleMapper.insert(role);
|
||||
}
|
||||
|
||||
private void createTenantAdminUser(Long tenantId, String adminUsername, String adminPassword, String tenantName) {
|
||||
String username = StringUtils.hasText(adminUsername) ? adminUsername.trim() : "tenant.admin";
|
||||
String rawPassword = StringUtils.hasText(adminPassword) ? adminPassword : "123456";
|
||||
SysUserDO existed = sysUserMapper.selectOne(Wrappers.<SysUserDO>lambdaQuery()
|
||||
.eq(SysUserDO::getTenantId, tenantId)
|
||||
.eq(SysUserDO::getUsername, username)
|
||||
.eq(SysUserDO::getDeleted, 0)
|
||||
.last("LIMIT 1"));
|
||||
if (existed != null) {
|
||||
return;
|
||||
}
|
||||
SysUserDO user = new SysUserDO();
|
||||
user.setTenantId(tenantId);
|
||||
user.setUserType("TENANT");
|
||||
user.setUsername(username);
|
||||
user.setPasswordHash(passwordEncoder.encode(rawPassword));
|
||||
user.setRealName(resolveAdminDisplayName(tenantName));
|
||||
user.setNickName(resolveAdminDisplayName(tenantName));
|
||||
user.setGender("UNKNOWN");
|
||||
user.setUserStatus("ENABLED");
|
||||
user.setMustUpdatePassword(1);
|
||||
user.setCreatedBy(1L);
|
||||
user.setUpdatedBy(1L);
|
||||
user.setDeleted(0);
|
||||
sysUserMapper.insert(user);
|
||||
|
||||
SysRoleDO adminRole = sysRoleMapper.selectOne(Wrappers.<SysRoleDO>lambdaQuery()
|
||||
.eq(SysRoleDO::getTenantId, tenantId)
|
||||
.eq(SysRoleDO::getRoleCode, "TENANT_ADMIN")
|
||||
.eq(SysRoleDO::getDeleted, 0)
|
||||
.last("LIMIT 1"));
|
||||
if (adminRole != null) {
|
||||
SysUserRoleDO userRole = new SysUserRoleDO();
|
||||
userRole.setTenantId(tenantId);
|
||||
userRole.setUserId(user.getId());
|
||||
userRole.setRoleId(adminRole.getId());
|
||||
userRole.setCreatedBy(1L);
|
||||
sysUserRoleMapper.insert(userRole);
|
||||
}
|
||||
}
|
||||
|
||||
private String generateTenantCode(String tenantName, String miniappAppId) {
|
||||
if (StringUtils.hasText(miniappAppId)) {
|
||||
return "tenant-" + miniappAppId.trim().toLowerCase().replaceAll("[^a-z0-9]", "");
|
||||
}
|
||||
return "tenant-" + System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private String resolveAdminDisplayName(String tenantName) {
|
||||
if (!StringUtils.hasText(tenantName)) {
|
||||
return "租户管理员";
|
||||
}
|
||||
return tenantName.length() > 24 ? tenantName.substring(0, 24) : tenantName;
|
||||
}
|
||||
|
||||
private TenantDetailResp toResp(TenantDO tenant, TenantMiniappConfigDO miniappConfig) {
|
||||
TenantDetailResp resp = new TenantDetailResp();
|
||||
resp.setId(tenant.getId());
|
||||
resp.setTenantCode(tenant.getTenantCode());
|
||||
resp.setTenantName(tenant.getTenantName());
|
||||
resp.setTenantShortName(tenant.getTenantShortName());
|
||||
resp.setUserLimit(tenant.getUserLimit());
|
||||
resp.setStorageLimitMb(tenant.getStorageLimitMb());
|
||||
resp.setContactName(tenant.getContactName());
|
||||
resp.setContactPhone(tenant.getContactPhone());
|
||||
if (miniappConfig != null) {
|
||||
resp.setMiniappAppId(miniappConfig.getMiniappAppId());
|
||||
resp.setMiniappName(miniappConfig.getMiniappName());
|
||||
resp.setMiniappOriginalId(miniappConfig.getMiniappOriginalId());
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.easycard.module.tenant.vo;
|
||||
|
||||
public class TenantDetailResp {
|
||||
|
||||
private Long id;
|
||||
private String tenantCode;
|
||||
private String tenantName;
|
||||
private String tenantShortName;
|
||||
private Integer userLimit;
|
||||
private Integer storageLimitMb;
|
||||
private String contactName;
|
||||
private String contactPhone;
|
||||
private String miniappAppId;
|
||||
private String miniappName;
|
||||
private String miniappOriginalId;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTenantCode() {
|
||||
return tenantCode;
|
||||
}
|
||||
|
||||
public void setTenantCode(String tenantCode) {
|
||||
this.tenantCode = tenantCode;
|
||||
}
|
||||
|
||||
public String getTenantName() {
|
||||
return tenantName;
|
||||
}
|
||||
|
||||
public void setTenantName(String tenantName) {
|
||||
this.tenantName = tenantName;
|
||||
}
|
||||
|
||||
public String getTenantShortName() {
|
||||
return tenantShortName;
|
||||
}
|
||||
|
||||
public void setTenantShortName(String tenantShortName) {
|
||||
this.tenantShortName = tenantShortName;
|
||||
}
|
||||
|
||||
public Integer getUserLimit() {
|
||||
return userLimit;
|
||||
}
|
||||
|
||||
public void setUserLimit(Integer userLimit) {
|
||||
this.userLimit = userLimit;
|
||||
}
|
||||
|
||||
public Integer getStorageLimitMb() {
|
||||
return storageLimitMb;
|
||||
}
|
||||
|
||||
public void setStorageLimitMb(Integer storageLimitMb) {
|
||||
this.storageLimitMb = storageLimitMb;
|
||||
}
|
||||
|
||||
public String getContactName() {
|
||||
return contactName;
|
||||
}
|
||||
|
||||
public void setContactName(String contactName) {
|
||||
this.contactName = contactName;
|
||||
}
|
||||
|
||||
public String getContactPhone() {
|
||||
return contactPhone;
|
||||
}
|
||||
|
||||
public void setContactPhone(String contactPhone) {
|
||||
this.contactPhone = contactPhone;
|
||||
}
|
||||
|
||||
public String getMiniappAppId() {
|
||||
return miniappAppId;
|
||||
}
|
||||
|
||||
public void setMiniappAppId(String miniappAppId) {
|
||||
this.miniappAppId = miniappAppId;
|
||||
}
|
||||
|
||||
public String getMiniappName() {
|
||||
return miniappName;
|
||||
}
|
||||
|
||||
public void setMiniappName(String miniappName) {
|
||||
this.miniappName = miniappName;
|
||||
}
|
||||
|
||||
public String getMiniappOriginalId() {
|
||||
return miniappOriginalId;
|
||||
}
|
||||
|
||||
public void setMiniappOriginalId(String miniappOriginalId) {
|
||||
this.miniappOriginalId = miniappOriginalId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.easycard.module.tenant.web;
|
||||
|
||||
import com.easycard.common.api.ApiResponse;
|
||||
import com.easycard.common.tenant.TenantContext;
|
||||
import com.easycard.common.tenant.TenantContextHolder;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.easycard.module.tenant.service.PlatformTenantService;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
public class MiniappTenantContextFilter extends OncePerRequestFilter {
|
||||
|
||||
private static final String MINIAPP_APP_ID_HEADER = "X-Miniapp-Appid";
|
||||
|
||||
private final PlatformTenantService platformTenantService;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public MiniappTenantContextFilter(PlatformTenantService platformTenantService, ObjectMapper objectMapper) {
|
||||
this.platformTenantService = platformTenantService;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldNotFilter(HttpServletRequest request) {
|
||||
return !request.getRequestURI().startsWith("/api/open/");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
String miniappAppId = request.getHeader(MINIAPP_APP_ID_HEADER);
|
||||
if (!StringUtils.hasText(miniappAppId)) {
|
||||
writeError(response, HttpServletResponse.SC_BAD_REQUEST,
|
||||
ApiResponse.fail("MINIAPP_APP_ID_MISSING", "缺少请求头 X-Miniapp-Appid"));
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<TenantContext> tenantContextOptional = platformTenantService.resolveTenantContextByMiniappAppId(miniappAppId);
|
||||
if (tenantContextOptional.isEmpty()) {
|
||||
writeError(response, HttpServletResponse.SC_NOT_FOUND,
|
||||
ApiResponse.fail("TENANT_MINIAPP_NOT_FOUND", "未找到对应的小程序租户配置"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
TenantContextHolder.set(tenantContextOptional.get());
|
||||
filterChain.doFilter(request, response);
|
||||
} finally {
|
||||
TenantContextHolder.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeError(HttpServletResponse response, int status, ApiResponse<Void> apiResponse) throws IOException {
|
||||
response.setStatus(status);
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
response.getWriter().write(objectMapper.writeValueAsString(apiResponse));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user