🔀 合并 dev 分支到 master
解决 9 个文件的冲突,取舍如下: - 导出模型:采用 dev 的 *ExportExcel 命名与拆分,并保留 master 的 @DateTimeFormat(dev 改名时漏加,会导致时间列显示为 Date.toString)。 - PortTerminal 导入:保留 master 的两阶段导入 + ImportFailureException 全量回滚(ImportFailureException 仅 master 有,合并后的 controller 依赖它), 导出失败明细改用 dev 的 exportFailureReasonOnly(仅标红失败原因列)。 - PortTerminal 导入模板:采用 dev 的"港口编码/码头编码"两列结构, 相应补上 resolveImportCode 归并规则,并在构建实体时显式赋 code/parentCode。 - PortTerminal 导出:采用 dev 的 PortTerminalExportExcel(接口已如此声明), 并保留 updateUserName 审计人翻译。 - 违章记录导入:保留 dev 的多错误收集 + 导入失败明细导出流水线, 删除已被拆列取代的 violationTypeOrItem 映射,补上 clearIrrelevantField, 并为导入校验补齐"对侧字段应留空"规则以与表单校验一致。 验证:mvn compile -DskipTests 全模块 BUILD SUCCESS。
This commit is contained in:
+6
-1
@@ -28,17 +28,22 @@ package org.springblade.system;
|
||||
import org.springblade.core.cloud.client.BladeCloudApplication;
|
||||
import org.springblade.core.launch.BladeApplication;
|
||||
import org.springblade.core.launch.constant.AppConstant;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springblade.system.props.IamSyncProperties;
|
||||
|
||||
/**
|
||||
* 系统模块启动器
|
||||
* @author Chill
|
||||
*/
|
||||
@ComponentScan(basePackages = {"org.springblade.system", "org.springblade.process"})
|
||||
@EnableConfigurationProperties(IamSyncProperties.class)
|
||||
@BladeCloudApplication
|
||||
public class SystemApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
BladeApplication.disableNacosLaunchConfig();
|
||||
BladeApplication.run(AppConstant.APPLICATION_SYSTEM_NAME, SystemApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -44,6 +44,7 @@ import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.excel.AirportMasterExcel;
|
||||
import org.springblade.system.excel.AirportMasterExportExcel;
|
||||
import org.springblade.system.excel.AirportMasterImporter;
|
||||
import org.springblade.system.pojo.entity.AirportMaster;
|
||||
import org.springblade.system.pojo.vo.AirportMasterVO;
|
||||
@@ -158,7 +159,7 @@ public class AirportMasterController extends BladeController {
|
||||
}
|
||||
List<AirportMasterExcel> failureList = airportMasterService.importAirportMaster(ExcelUtil.read(file, AirportMasterExcel.class));
|
||||
if (Func.isNotEmpty(failureList)) {
|
||||
org.springblade.common.excel.ImportFailureExcelUtil.export(response, "空港机场主数据导入失败明细" + DateUtil.time(), "导入失败明细", failureList, AirportMasterExcel.class);
|
||||
org.springblade.common.excel.ImportFailureExcelUtil.exportFailureReasonOnly(response, "空港机场主数据导入失败明细" + DateUtil.time(), "导入失败明细", failureList, AirportMasterExcel.class);
|
||||
return null;
|
||||
}
|
||||
return R.success("操作成功");
|
||||
@@ -180,8 +181,8 @@ public class AirportMasterController extends BladeController {
|
||||
if (Func.isNotEmpty(ids)) {
|
||||
queryWrapper.lambda().in(AirportMaster::getId, Func.toLongList(ids.toString()));
|
||||
}
|
||||
List<AirportMasterExcel> list = airportMasterService.exportAirportMaster(queryWrapper);
|
||||
ExcelUtil.export(response, "空港机场主数据" + DateUtil.time(), "空港机场主数据表", list, AirportMasterExcel.class);
|
||||
List<AirportMasterExportExcel> list = airportMasterService.exportAirportMaster(queryWrapper);
|
||||
ExcelUtil.export(response, "空港机场主数据" + DateUtil.time(), "空港机场主数据表", list, AirportMasterExportExcel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+54
-3
@@ -50,8 +50,10 @@ import org.springblade.system.pojo.entity.Dept;
|
||||
import org.springblade.system.pojo.entity.User;
|
||||
import org.springblade.system.pojo.enums.DictEnum;
|
||||
import org.springblade.system.pojo.vo.DeptVO;
|
||||
import org.springblade.system.pojo.vo.OaOrgSyncPageVO;
|
||||
import org.springblade.system.pojo.vo.UserVO;
|
||||
import org.springblade.system.service.IDeptService;
|
||||
import org.springblade.system.service.IOASyncService;
|
||||
import org.springblade.system.wrapper.DeptWrapper;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@@ -73,6 +75,7 @@ import static org.springblade.core.cache.constant.CacheConstant.SYS_CACHE;
|
||||
public class DeptController extends BladeController {
|
||||
|
||||
private final IDeptService deptService;
|
||||
private final IOASyncService oaSyncService;
|
||||
|
||||
/**
|
||||
* 详情
|
||||
@@ -159,12 +162,49 @@ public class DeptController extends BladeController {
|
||||
return R.fail("操作失败");
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步IAM组织。
|
||||
*/
|
||||
@IsAdmin
|
||||
@PostMapping("/sync-iam-organizations")
|
||||
@ApiOperationSupport(order = 7)
|
||||
@Operation(summary = "同步IAM组织")
|
||||
public R<Integer> syncIamOrganizations() {
|
||||
return R.data(deptService.syncIamOrganizations());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从OA按页同步公司
|
||||
*/
|
||||
@IsAdmin
|
||||
@PostMapping("/sync-oa-company")
|
||||
@ApiOperationSupport(order = 8)
|
||||
@Operation(summary = "同步OA公司")
|
||||
public R<OaOrgSyncPageVO> syncOaCompany(
|
||||
@RequestParam(defaultValue = "1") Integer current,
|
||||
@RequestParam(defaultValue = "20") Integer size) {
|
||||
return R.data(oaSyncService.syncCompanyPage(current, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从OA按页同步部门(需先完成公司同步)
|
||||
*/
|
||||
@IsAdmin
|
||||
@PostMapping("/sync-oa-department")
|
||||
@ApiOperationSupport(order = 9)
|
||||
@Operation(summary = "同步OA部门")
|
||||
public R<OaOrgSyncPageVO> syncOaDepartment(
|
||||
@RequestParam(defaultValue = "1") Integer current,
|
||||
@RequestParam(defaultValue = "20") Integer size) {
|
||||
return R.data(oaSyncService.syncDepartmentPage(current, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
@IsAdmin
|
||||
@PostMapping("/remove")
|
||||
@ApiOperationSupport(order = 7)
|
||||
@ApiOperationSupport(order = 10)
|
||||
@Operation(summary = "删除", description = "传入ids")
|
||||
public R remove(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
|
||||
CacheUtil.clear(SYS_CACHE);
|
||||
@@ -177,7 +217,7 @@ public class DeptController extends BladeController {
|
||||
*/
|
||||
@PreAuth(AuthConstant.PERMIT_ALL)
|
||||
@GetMapping("/select")
|
||||
@ApiOperationSupport(order = 8)
|
||||
@ApiOperationSupport(order = 11)
|
||||
@Operation(summary = "下拉数据源", description = "传入id集合")
|
||||
public R<List<Dept>> select(Long userId, String deptId) {
|
||||
if (Func.isNotEmpty(userId)) {
|
||||
@@ -189,12 +229,23 @@ public class DeptController extends BladeController {
|
||||
return R.data(deptService.selectDept(deptId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台公司下拉(是否平台公司=是)
|
||||
*/
|
||||
@PreAuth(AuthConstant.PERMIT_ALL)
|
||||
@GetMapping("/platform-company-select")
|
||||
@ApiOperationSupport(order = 12)
|
||||
@Operation(summary = "平台公司下拉", description = "返回是否平台公司=是的部门列表")
|
||||
public R<List<Dept>> platformCompanySelect() {
|
||||
return R.data(deptService.listPlatformCompany());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门的主管信息
|
||||
*/
|
||||
@IsAdmin
|
||||
@GetMapping("/dept-leader-info")
|
||||
@ApiOperationSupport(order = 9)
|
||||
@ApiOperationSupport(order = 13)
|
||||
@Operation(summary = "获取部门的主管信息", description = "传入deptId")
|
||||
public R<List<UserVO>> deptLeaderInfo(@Parameter(description = "部门id", required = true) @RequestParam Long deptId) {
|
||||
List<UserVO> list = deptService.deptLeaderInfo(deptId);
|
||||
|
||||
+3
-2
@@ -46,6 +46,7 @@ import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.excel.FeeItemExcel;
|
||||
import org.springblade.system.excel.FeeItemExportExcel;
|
||||
import org.springblade.system.excel.FeeItemImportFailureExcel;
|
||||
import org.springblade.system.pojo.entity.FeeItem;
|
||||
import org.springblade.system.pojo.vo.FeeItemVO;
|
||||
@@ -155,8 +156,8 @@ public class FeeItemController extends BladeController {
|
||||
public void exportFeeItem(FeeItemVO feeItem,
|
||||
@RequestParam(required = false) String ids,
|
||||
HttpServletResponse response) {
|
||||
List<FeeItemExcel> list = feeItemService.exportFeeItem(buildExportQuery(feeItem, ids));
|
||||
ExcelUtil.export(response, "费用项" + DateUtil.time(), "费用项表", list, FeeItemExcel.class);
|
||||
List<FeeItemExportExcel> list = feeItemService.exportFeeItem(buildExportQuery(feeItem, ids));
|
||||
ExcelUtil.export(response, "费用项" + DateUtil.time(), "费用项表", list, FeeItemExportExcel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments from this software for such purposes. Copyright of this software remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.boot.ctrl.BladeController;
|
||||
import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.mp.support.Query;
|
||||
import org.springblade.core.secure.annotation.PreAuth;
|
||||
import org.springblade.core.tenant.annotation.NonDS;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.pojo.entity.InvoiceItem;
|
||||
import org.springblade.system.pojo.vo.InvoiceItemVO;
|
||||
import org.springblade.system.service.IInvoiceItemService;
|
||||
import org.springblade.system.wrapper.InvoiceItemWrapper;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 开票项目控制器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@NonDS
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@PreAuth(menu = "invoice_item")
|
||||
@RequestMapping("/invoice-item")
|
||||
@Tag(name = "开票项目", description = "开票项目")
|
||||
public class InvoiceItemController extends BladeController {
|
||||
|
||||
private final IInvoiceItemService invoiceItemService;
|
||||
|
||||
@GetMapping("/detail")
|
||||
@ApiOperationSupport(order = 1)
|
||||
@Operation(summary = "详情", description = "传入invoiceItem")
|
||||
public R<InvoiceItemVO> detail(InvoiceItem invoiceItem) {
|
||||
InvoiceItem detail = invoiceItemService.getOne(Condition.getQueryWrapper(invoiceItem));
|
||||
return R.data(InvoiceItemWrapper.build().entityVO(detail));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "分页", description = "传入invoiceItem")
|
||||
public R<IPage<InvoiceItemVO>> list(InvoiceItemVO invoiceItem, Query query) {
|
||||
return R.data(invoiceItemService.selectInvoiceItemPage(Condition.getPage(query), invoiceItem));
|
||||
}
|
||||
|
||||
@PostMapping("/submit")
|
||||
@ApiOperationSupport(order = 3)
|
||||
@Operation(summary = "新增或修改", description = "传入invoiceItem")
|
||||
public R submit(@Valid @RequestBody InvoiceItem invoiceItem) {
|
||||
return R.status(invoiceItemService.submit(invoiceItem));
|
||||
}
|
||||
|
||||
@PostMapping("/remove")
|
||||
@ApiOperationSupport(order = 4)
|
||||
@Operation(summary = "逻辑删除", description = "传入ids")
|
||||
public R remove(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
|
||||
return R.status(invoiceItemService.deleteLogic(Func.toLongList(ids)));
|
||||
}
|
||||
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.boot.ctrl.BladeController;
|
||||
import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.mp.support.Query;
|
||||
import org.springblade.core.secure.annotation.PreAuth;
|
||||
import org.springblade.core.tenant.annotation.NonDS;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.pojo.entity.MeasurementUnit;
|
||||
import org.springblade.system.pojo.vo.MeasurementUnitVO;
|
||||
import org.springblade.system.service.IMeasurementUnitService;
|
||||
import org.springblade.system.wrapper.MeasurementUnitWrapper;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 计量单位控制器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@NonDS
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@PreAuth(menu = "measurement_unit")
|
||||
@RequestMapping("/measurement-unit")
|
||||
@Tag(name = "计量单位", description = "计量单位")
|
||||
public class MeasurementUnitController extends BladeController {
|
||||
|
||||
private final IMeasurementUnitService measurementUnitService;
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*
|
||||
* @param measurementUnit 查询条件
|
||||
* @return 计量单位详情
|
||||
*/
|
||||
@GetMapping("/detail")
|
||||
@ApiOperationSupport(order = 1)
|
||||
@Operation(summary = "详情", description = "传入measurementUnit")
|
||||
public R<MeasurementUnitVO> detail(MeasurementUnit measurementUnit) {
|
||||
MeasurementUnit detail = measurementUnitService.getOne(Condition.getQueryWrapper(measurementUnit));
|
||||
if (detail == null) {
|
||||
return R.fail("数据不存在");
|
||||
}
|
||||
return R.data(MeasurementUnitWrapper.build().entityVO(detail));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*
|
||||
* @param measurementUnit 查询条件
|
||||
* @param query 分页参数
|
||||
* @return 计量单位分页
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "分页", description = "传入measurementUnit")
|
||||
public R<IPage<MeasurementUnitVO>> list(MeasurementUnitVO measurementUnit, Query query) {
|
||||
IPage<MeasurementUnitVO> pages = measurementUnitService.selectMeasurementUnitPage(
|
||||
Condition.getPage(query), measurementUnit
|
||||
);
|
||||
return R.data(pages);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增或修改
|
||||
*
|
||||
* @param measurementUnit 计量单位
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PostMapping("/submit")
|
||||
@ApiOperationSupport(order = 3)
|
||||
@Operation(summary = "新增或修改", description = "传入measurementUnit")
|
||||
public R submit(@Valid @RequestBody MeasurementUnit measurementUnit) {
|
||||
return R.status(measurementUnitService.submit(measurementUnit));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @param ids 主键集合
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PostMapping("/remove")
|
||||
@ApiOperationSupport(order = 4)
|
||||
@Operation(summary = "逻辑删除", description = "传入ids")
|
||||
public R remove(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
|
||||
return R.status(measurementUnitService.deleteLogic(Func.toLongList(ids)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用或停用
|
||||
*
|
||||
* @param id 主键
|
||||
* @param status 状态
|
||||
* @return 操作结果
|
||||
*/
|
||||
@PostMapping("/status")
|
||||
@ApiOperationSupport(order = 5)
|
||||
@Operation(summary = "启用或停用", description = "传入id和status")
|
||||
public R status(@Parameter(description = "主键", required = true) @RequestParam Long id,
|
||||
@Parameter(description = "状态", required = true) @RequestParam Integer status) {
|
||||
return R.status(measurementUnitService.changeStatus(id, status));
|
||||
}
|
||||
|
||||
}
|
||||
+276
-257
@@ -1,257 +1,276 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.boot.ctrl.BladeController;
|
||||
import org.springblade.core.excel.util.ExcelUtil;
|
||||
import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.mp.support.Query;
|
||||
import org.springblade.core.secure.annotation.PreAuth;
|
||||
import org.springblade.core.tenant.annotation.NonDS;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.common.excel.ImportFailureExcelUtil;
|
||||
import org.springblade.system.excel.ImportFailureException;
|
||||
import org.springblade.system.excel.PortTerminalExcel;
|
||||
import org.springblade.system.excel.PortTerminalImporter;
|
||||
import org.springblade.system.pojo.entity.PortTerminal;
|
||||
import org.springblade.system.pojo.vo.PortTerminalVO;
|
||||
import org.springblade.system.service.IPortTerminalService;
|
||||
import org.springblade.system.wrapper.PortTerminalWrapper;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 港口码头主数据 控制器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@NonDS
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@PreAuth(menu = "port_terminal")
|
||||
@RequestMapping("/port-terminal")
|
||||
@Tag(name = "港口码头主数据", description = "港口码头主数据")
|
||||
public class PortTerminalController extends BladeController {
|
||||
|
||||
private static final int DEFAULT_CURRENT = 1;
|
||||
private static final int DEFAULT_SIZE = 10;
|
||||
private static final int MAX_SIZE = 100;
|
||||
private static final String SOURCE_INITIAL = "初始化导入";
|
||||
|
||||
private final IPortTerminalService portTerminalService;
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
@GetMapping("/detail")
|
||||
@ApiOperationSupport(order = 1)
|
||||
@Operation(summary = "详情", description = "传入portTerminal")
|
||||
public R<PortTerminalVO> detail(PortTerminal portTerminal) {
|
||||
if (Func.isEmpty(portTerminal.getId())) {
|
||||
return R.fail("主键不能为空");
|
||||
}
|
||||
PortTerminal detail = portTerminalService.getById(portTerminal.getId());
|
||||
if (Func.isEmpty(detail)) {
|
||||
return R.fail("港口码头不存在");
|
||||
}
|
||||
return R.data(PortTerminalWrapper.build().entityVO(detail));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "分页", description = "传入portTerminal")
|
||||
public R<IPage<PortTerminalVO>> list(PortTerminalVO portTerminal, Query query) {
|
||||
IPage<PortTerminalVO> pages = portTerminalService.selectPortTerminalPage(Condition.getPage(normalizeQuery(query)), portTerminal);
|
||||
return R.data(pages);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增或修改
|
||||
*/
|
||||
@PostMapping("/submit")
|
||||
@ApiOperationSupport(order = 3)
|
||||
@Operation(summary = "新增或修改", description = "传入portTerminal")
|
||||
public R submit(@Valid @RequestBody PortTerminal portTerminal) {
|
||||
return R.status(portTerminalService.submit(portTerminal));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
@PostMapping("/remove")
|
||||
@ApiOperationSupport(order = 4)
|
||||
@Operation(summary = "逻辑删除", description = "传入ids")
|
||||
public R remove(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
|
||||
if (Func.isEmpty(ids)) {
|
||||
return R.fail("主键不能为空");
|
||||
}
|
||||
return R.status(portTerminalService.deleteLogic(Func.toLongList(ids)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用或停用
|
||||
*/
|
||||
@PostMapping("/status")
|
||||
@ApiOperationSupport(order = 5)
|
||||
@Operation(summary = "启用或停用", description = "传入id和status")
|
||||
public R status(@Parameter(description = "主键", required = true) @RequestParam Long id,
|
||||
@Parameter(description = "状态", required = true) @RequestParam Integer status) {
|
||||
return R.status(portTerminalService.changeStatus(id, status));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上级港口下拉数据源
|
||||
*/
|
||||
@GetMapping("/port-select")
|
||||
@ApiOperationSupport(order = 6)
|
||||
@Operation(summary = "上级港口下拉数据源")
|
||||
public R<List<PortTerminal>> portSelect() {
|
||||
return R.data(portTerminalService.selectEnabledPorts());
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入港口码头主数据
|
||||
*/
|
||||
@PostMapping("/import-port-terminal")
|
||||
@ApiOperationSupport(order = 7)
|
||||
@Operation(summary = "导入港口码头主数据", description = "传入excel")
|
||||
public R importPortTerminal(MultipartFile file, HttpServletResponse response) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
return R.fail("上传文件不能为空");
|
||||
}
|
||||
String fileName = Func.toStrWithEmpty(file.getOriginalFilename(), "").toLowerCase();
|
||||
if (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")) {
|
||||
return R.fail("请上传 .xls,.xlsx 标准格式文件");
|
||||
}
|
||||
try {
|
||||
portTerminalService.importPortTerminal(ExcelUtil.read(file, PortTerminalExcel.class));
|
||||
} catch (ImportFailureException exception) {
|
||||
// 全失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。
|
||||
exportFailure(response, exception.getFailureList());
|
||||
return null;
|
||||
}
|
||||
return R.success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出导入失败明细,内容为原表全部数据并在末尾追加失败原因列。
|
||||
*/
|
||||
private void exportFailure(HttpServletResponse response, List<?> failureList) {
|
||||
ImportFailureExcelUtil.export(response, "港口码头主数据导入失败明细" + DateUtil.time(), "导入失败明细", failureList, PortTerminalExcel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出港口码头主数据
|
||||
*/
|
||||
@GetMapping("/export-port-terminal")
|
||||
@ApiOperationSupport(order = 8)
|
||||
@Operation(summary = "导出港口码头主数据")
|
||||
public void exportPortTerminal(@Parameter(hidden = true) @RequestParam Map<String, Object> portTerminal, HttpServletResponse response) {
|
||||
Object ids = portTerminal.remove("ids");
|
||||
Object dataSource = portTerminal.remove("dataSource");
|
||||
portTerminal.remove("Blade-Auth");
|
||||
portTerminal.remove("Authorization");
|
||||
portTerminal.remove("access_token");
|
||||
normalizeRegionCodeCondition(portTerminal);
|
||||
QueryWrapper<PortTerminal> queryWrapper = Condition.getQueryWrapper(portTerminal, PortTerminal.class);
|
||||
applyDataSourceCondition(queryWrapper, dataSource);
|
||||
if (Func.isNotEmpty(ids)) {
|
||||
queryWrapper.lambda().in(PortTerminal::getId, Func.toLongList(ids.toString()));
|
||||
}
|
||||
List<PortTerminalExcel> list = portTerminalService.exportPortTerminal(queryWrapper);
|
||||
ExcelUtil.export(response, "港口码头主数据" + DateUtil.time(), "港口码头主数据表", list, PortTerminalExcel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出模板
|
||||
*/
|
||||
@GetMapping("/export-template")
|
||||
@ApiOperationSupport(order = 9)
|
||||
@Operation(summary = "导出模板")
|
||||
public void exportTemplate(HttpServletResponse response) {
|
||||
List<PortTerminalExcel> list = new ArrayList<>();
|
||||
ExcelUtil.export(response, "港口码头主数据模板", "港口码头主数据表", list, PortTerminalExcel.class);
|
||||
}
|
||||
|
||||
private Query normalizeQuery(Query query) {
|
||||
if (query == null) {
|
||||
query = new Query();
|
||||
}
|
||||
if (query.getCurrent() == null || query.getCurrent() < DEFAULT_CURRENT) {
|
||||
query.setCurrent(DEFAULT_CURRENT);
|
||||
}
|
||||
if (query.getSize() == null || query.getSize() <= 0) {
|
||||
query.setSize(DEFAULT_SIZE);
|
||||
}
|
||||
if (query.getSize() > MAX_SIZE) {
|
||||
query.setSize(MAX_SIZE);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
private void normalizeRegionCodeCondition(Map<String, Object> params) {
|
||||
Object regionCode = params.remove("regionCode");
|
||||
if (Func.isNotEmpty(regionCode) && Func.isEmpty(params.get("districtCode"))) {
|
||||
params.put("districtCode", regionCode);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyDataSourceCondition(QueryWrapper<PortTerminal> queryWrapper, Object dataSource) {
|
||||
String value = Func.toStrWithEmpty(dataSource, "");
|
||||
if (Func.isEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
if (SOURCE_INITIAL.equals(value)) {
|
||||
queryWrapper.in("data_source", SOURCE_INITIAL, "初始导入");
|
||||
} else {
|
||||
queryWrapper.eq("data_source", value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.boot.ctrl.BladeController;
|
||||
import org.springblade.core.excel.util.ExcelUtil;
|
||||
import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.mp.support.Query;
|
||||
import org.springblade.core.secure.annotation.PreAuth;
|
||||
import org.springblade.core.tenant.annotation.NonDS;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.common.excel.ImportFailureExcelUtil;
|
||||
import org.springblade.system.excel.ImportFailureException;
|
||||
import org.springblade.system.excel.PortTerminalExcel;
|
||||
import org.springblade.system.excel.PortTerminalExportExcel;
|
||||
import org.springblade.system.excel.PortTerminalImporter;
|
||||
import org.springblade.system.pojo.entity.PortTerminal;
|
||||
import org.springblade.system.pojo.vo.PortTerminalVO;
|
||||
import org.springblade.system.service.IPortTerminalService;
|
||||
import org.springblade.system.wrapper.PortTerminalWrapper;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 港口码头主数据 控制器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@NonDS
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@PreAuth(menu = "port_terminal")
|
||||
@RequestMapping("/port-terminal")
|
||||
@Tag(name = "港口码头主数据", description = "港口码头主数据")
|
||||
public class PortTerminalController extends BladeController {
|
||||
|
||||
private static final int DEFAULT_CURRENT = 1;
|
||||
private static final int DEFAULT_SIZE = 10;
|
||||
private static final int MAX_SIZE = 100;
|
||||
private static final String SOURCE_INITIAL = "初始化录入";
|
||||
private static final String SOURCE_INITIAL_IMPORT = "初始化导入";
|
||||
private static final String SOURCE_INITIAL_OLD = "初始导入";
|
||||
private static final String SOURCE_MANUAL = "手动录入";
|
||||
private static final String SOURCE_MANUAL_OLD = "手工导入";
|
||||
|
||||
private final IPortTerminalService portTerminalService;
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
@GetMapping("/detail")
|
||||
@ApiOperationSupport(order = 1)
|
||||
@Operation(summary = "详情", description = "传入portTerminal")
|
||||
public R<PortTerminalVO> detail(PortTerminal portTerminal) {
|
||||
if (Func.isEmpty(portTerminal.getId())) {
|
||||
return R.fail("主键不能为空");
|
||||
}
|
||||
PortTerminal detail = portTerminalService.getById(portTerminal.getId());
|
||||
if (Func.isEmpty(detail)) {
|
||||
return R.fail("港口码头不存在");
|
||||
}
|
||||
detail.setDataSource(normalizeDataSource(detail.getDataSource()));
|
||||
return R.data(PortTerminalWrapper.build().entityVO(detail));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "分页", description = "传入portTerminal")
|
||||
public R<IPage<PortTerminalVO>> list(PortTerminalVO portTerminal, Query query) {
|
||||
IPage<PortTerminalVO> pages = portTerminalService.selectPortTerminalPage(Condition.getPage(normalizeQuery(query)), portTerminal);
|
||||
return R.data(pages);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增或修改
|
||||
*/
|
||||
@PostMapping("/submit")
|
||||
@ApiOperationSupport(order = 3)
|
||||
@Operation(summary = "新增或修改", description = "传入portTerminal")
|
||||
public R submit(@Valid @RequestBody PortTerminal portTerminal) {
|
||||
return R.status(portTerminalService.submit(portTerminal));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
@PostMapping("/remove")
|
||||
@ApiOperationSupport(order = 4)
|
||||
@Operation(summary = "逻辑删除", description = "传入ids")
|
||||
public R remove(@Parameter(description = "主键集合", required = true) @RequestParam String ids) {
|
||||
if (Func.isEmpty(ids)) {
|
||||
return R.fail("主键不能为空");
|
||||
}
|
||||
return R.status(portTerminalService.deleteLogic(Func.toLongList(ids)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用或停用
|
||||
*/
|
||||
@PostMapping("/status")
|
||||
@ApiOperationSupport(order = 5)
|
||||
@Operation(summary = "启用或停用", description = "传入id和status")
|
||||
public R status(@Parameter(description = "主键", required = true) @RequestParam Long id,
|
||||
@Parameter(description = "状态", required = true) @RequestParam Integer status) {
|
||||
return R.status(portTerminalService.changeStatus(id, status));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上级港口下拉数据源
|
||||
*/
|
||||
@GetMapping("/port-select")
|
||||
@ApiOperationSupport(order = 6)
|
||||
@Operation(summary = "上级港口下拉数据源")
|
||||
public R<List<PortTerminal>> portSelect() {
|
||||
List<PortTerminal> ports = portTerminalService.selectEnabledPorts();
|
||||
ports.forEach(port -> port.setDataSource(normalizeDataSource(port.getDataSource())));
|
||||
return R.data(ports);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入港口码头主数据
|
||||
*/
|
||||
@PostMapping("/import-port-terminal")
|
||||
@ApiOperationSupport(order = 7)
|
||||
@Operation(summary = "导入港口码头主数据", description = "传入excel")
|
||||
public R importPortTerminal(MultipartFile file, HttpServletResponse response) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
return R.fail("上传文件不能为空");
|
||||
}
|
||||
String fileName = Func.toStrWithEmpty(file.getOriginalFilename(), "").toLowerCase();
|
||||
if (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")) {
|
||||
return R.fail("请上传 .xls,.xlsx 标准格式文件");
|
||||
}
|
||||
try {
|
||||
portTerminalService.importPortTerminal(ExcelUtil.read(file, PortTerminalExcel.class));
|
||||
} catch (ImportFailureException exception) {
|
||||
// 全失败即整批回滚,导出原表全部数据并标注错误,用户修正后重新导入。
|
||||
exportFailure(response, exception.getFailureList());
|
||||
return null;
|
||||
}
|
||||
return R.success("操作成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出导入失败明细,内容为原表全部数据并在末尾追加失败原因列。
|
||||
* <p>
|
||||
* 失败数据仅标红出错单元格与失败原因列,表头保持默认样式。
|
||||
*/
|
||||
private void exportFailure(HttpServletResponse response, List<?> failureList) {
|
||||
ImportFailureExcelUtil.exportFailureReasonOnly(response, "港口码头主数据导入失败明细" + DateUtil.time(), "导入失败明细", failureList, PortTerminalExcel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出港口码头主数据
|
||||
*/
|
||||
@GetMapping("/export-port-terminal")
|
||||
@ApiOperationSupport(order = 8)
|
||||
@Operation(summary = "导出港口码头主数据")
|
||||
public void exportPortTerminal(@Parameter(hidden = true) @RequestParam Map<String, Object> portTerminal, HttpServletResponse response) {
|
||||
Object ids = portTerminal.remove("ids");
|
||||
Object dataSource = portTerminal.remove("dataSource");
|
||||
portTerminal.remove("Blade-Auth");
|
||||
portTerminal.remove("Authorization");
|
||||
portTerminal.remove("access_token");
|
||||
normalizeRegionCodeCondition(portTerminal);
|
||||
QueryWrapper<PortTerminal> queryWrapper = Condition.getQueryWrapper(portTerminal, PortTerminal.class);
|
||||
applyDataSourceCondition(queryWrapper, dataSource);
|
||||
if (Func.isNotEmpty(ids)) {
|
||||
queryWrapper.lambda().in(PortTerminal::getId, Func.toLongList(ids.toString()));
|
||||
}
|
||||
List<PortTerminalExportExcel> list = portTerminalService.exportPortTerminal(queryWrapper);
|
||||
ExcelUtil.export(response, "港口码头主数据" + DateUtil.time(), "港口码头主数据表", list, PortTerminalExportExcel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出模板
|
||||
*/
|
||||
@GetMapping("/export-template")
|
||||
@ApiOperationSupport(order = 9)
|
||||
@Operation(summary = "导出模板")
|
||||
public void exportTemplate(HttpServletResponse response) {
|
||||
List<PortTerminalExcel> list = new ArrayList<>();
|
||||
ExcelUtil.export(response, "港口码头主数据模板", "港口码头主数据表", list, PortTerminalExcel.class);
|
||||
}
|
||||
|
||||
private Query normalizeQuery(Query query) {
|
||||
if (query == null) {
|
||||
query = new Query();
|
||||
}
|
||||
if (query.getCurrent() == null || query.getCurrent() < DEFAULT_CURRENT) {
|
||||
query.setCurrent(DEFAULT_CURRENT);
|
||||
}
|
||||
if (query.getSize() == null || query.getSize() <= 0) {
|
||||
query.setSize(DEFAULT_SIZE);
|
||||
}
|
||||
if (query.getSize() > MAX_SIZE) {
|
||||
query.setSize(MAX_SIZE);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
private void normalizeRegionCodeCondition(Map<String, Object> params) {
|
||||
Object regionCode = params.remove("regionCode");
|
||||
if (Func.isNotEmpty(regionCode) && Func.isEmpty(params.get("districtCode"))) {
|
||||
params.put("districtCode", regionCode);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyDataSourceCondition(QueryWrapper<PortTerminal> queryWrapper, Object dataSource) {
|
||||
String value = Func.toStrWithEmpty(dataSource, "");
|
||||
if (Func.isEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
if (SOURCE_INITIAL.equals(value)) {
|
||||
queryWrapper.in("data_source", SOURCE_INITIAL, SOURCE_INITIAL_IMPORT, SOURCE_INITIAL_OLD);
|
||||
} else if (SOURCE_MANUAL.equals(value)) {
|
||||
queryWrapper.in("data_source", SOURCE_MANUAL, SOURCE_MANUAL_OLD);
|
||||
} else {
|
||||
queryWrapper.eq("data_source", value);
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeDataSource(String dataSource) {
|
||||
if (SOURCE_INITIAL_IMPORT.equals(dataSource) || SOURCE_INITIAL_OLD.equals(dataSource)) {
|
||||
return SOURCE_INITIAL;
|
||||
}
|
||||
return SOURCE_MANUAL_OLD.equals(dataSource) ? SOURCE_MANUAL : dataSource;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-3
@@ -44,6 +44,7 @@ import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.excel.RailwayStationExcel;
|
||||
import org.springblade.system.excel.RailwayStationExportExcel;
|
||||
import org.springblade.system.excel.RailwayStationImporter;
|
||||
import org.springblade.system.pojo.entity.RailwayStation;
|
||||
import org.springblade.system.pojo.vo.RailwayStationVO;
|
||||
@@ -161,7 +162,7 @@ public class RailwayStationController extends BladeController {
|
||||
}
|
||||
List<RailwayStationExcel> failureList = railwayStationService.importRailwayStation(ExcelUtil.read(file, RailwayStationExcel.class));
|
||||
if (Func.isNotEmpty(failureList)) {
|
||||
org.springblade.common.excel.ImportFailureExcelUtil.export(response, "铁路车站主数据导入失败明细" + DateUtil.time(), "导入失败明细", failureList, RailwayStationExcel.class);
|
||||
org.springblade.common.excel.ImportFailureExcelUtil.exportFailureReasonOnly(response, "铁路车站主数据导入失败明细" + DateUtil.time(), "导入失败明细", failureList, RailwayStationExcel.class);
|
||||
return null;
|
||||
}
|
||||
return R.success("操作成功");
|
||||
@@ -185,8 +186,8 @@ public class RailwayStationController extends BladeController {
|
||||
if (Func.isNotEmpty(ids)) {
|
||||
queryWrapper.lambda().in(RailwayStation::getId, Func.toLongList(ids.toString()));
|
||||
}
|
||||
List<RailwayStationExcel> list = railwayStationService.exportRailwayStation(queryWrapper);
|
||||
ExcelUtil.export(response, "铁路车站主数据" + DateUtil.time(), "铁路车站主数据表", list, RailwayStationExcel.class);
|
||||
List<RailwayStationExportExcel> list = railwayStationService.exportRailwayStation(queryWrapper);
|
||||
ExcelUtil.export(response, "铁路车站主数据" + DateUtil.time(), "铁路车站主数据表", list, RailwayStationExportExcel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-2
@@ -48,6 +48,7 @@ import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.system.pojo.entity.Region;
|
||||
import org.springblade.system.excel.RegionExcel;
|
||||
import org.springblade.system.excel.RegionExportExcel;
|
||||
import org.springblade.system.excel.RegionImporter;
|
||||
import org.springblade.system.service.IRegionService;
|
||||
import org.springblade.system.pojo.vo.RegionVO;
|
||||
@@ -206,8 +207,8 @@ public class RegionController extends BladeController {
|
||||
@Operation(summary = "导出行政区划", description = "传入user")
|
||||
public void exportRegion(@Parameter(hidden = true) @RequestParam Map<String, Object> region, HttpServletResponse response) {
|
||||
QueryWrapper<Region> queryWrapper = Condition.getQueryWrapper(region, Region.class);
|
||||
List<RegionExcel> list = regionService.exportRegion(queryWrapper);
|
||||
ExcelUtil.export(response, "行政区划数据" + DateUtil.time(), "行政区划数据表", list, RegionExcel.class);
|
||||
List<RegionExportExcel> list = regionService.exportRegion(queryWrapper);
|
||||
ExcelUtil.export(response, "行政区划数据" + DateUtil.time(), "行政区划数据表", list, RegionExportExcel.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+30
@@ -54,7 +54,9 @@ import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.system.excel.UserExcel;
|
||||
import org.springblade.system.excel.UserImporter;
|
||||
import org.springblade.system.pojo.entity.User;
|
||||
import org.springblade.system.pojo.vo.OaPersonSyncPageVO;
|
||||
import org.springblade.system.pojo.vo.UserVO;
|
||||
import org.springblade.system.service.IOASyncService;
|
||||
import org.springblade.system.service.IUserService;
|
||||
import org.springblade.system.wrapper.UserWrapper;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -77,6 +79,7 @@ import java.util.Map;
|
||||
public class UserController {
|
||||
|
||||
private final IUserService userService;
|
||||
private final IOASyncService oaSyncService;
|
||||
|
||||
/**
|
||||
* 查询单条
|
||||
@@ -157,6 +160,19 @@ public class UserController {
|
||||
return R.status(userService.submit(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从OA按页同步人员,并按公司/部门生成组织后绑定到三级部门。
|
||||
*/
|
||||
@IsAdmin
|
||||
@PostMapping("/sync-iam-accounts")
|
||||
@ApiOperationSupport(order = 6)
|
||||
@Operation(summary = "同步OA人员")
|
||||
public R<OaPersonSyncPageVO> syncIamAccounts(
|
||||
@RequestParam(defaultValue = "1") Integer current,
|
||||
@RequestParam(defaultValue = "20") Integer size) {
|
||||
return R.data(oaSyncService.syncPersonFromUserList(current, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改
|
||||
*/
|
||||
@@ -216,6 +232,20 @@ public class UserController {
|
||||
return R.status(temp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前用户设置/重置登录密码(小程序首次设密、短信验证后改密)
|
||||
* <p>
|
||||
* 对外路径:/blade-system/user/password ;网关别名 /blade-user/password 亦可到达。
|
||||
*/
|
||||
@PostMapping("/password")
|
||||
@ApiOperationSupport(order = 10)
|
||||
@Operation(summary = "设置登录密码", description = "当前登录用户设置密码,无需原密码")
|
||||
public R password(BladeUser user,
|
||||
@Parameter(description = "新密码", required = true) @RequestParam String password,
|
||||
@Parameter(description = "确认密码", required = true) @RequestParam String password2) {
|
||||
return R.status(userService.setPassword(user.getUserId(), password, password2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员修改密码
|
||||
*/
|
||||
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.controller;
|
||||
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.tenant.annotation.NonDS;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.system.pojo.dto.PhoneChangeDTO;
|
||||
import org.springblade.system.pojo.dto.PhoneVerifyDTO;
|
||||
import org.springblade.system.service.IUserPhoneService;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 用户手机号变更(小程序「修改手机号」)
|
||||
* <p>
|
||||
* 对外路径:/blade-system/user/phone/** ;网关别名 /blade-user/phone/** 亦可到达。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@NonDS
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping("/user/phone")
|
||||
@Tag(name = "用户手机号", description = "修改手机号")
|
||||
public class UserPhoneController {
|
||||
|
||||
private final IUserPhoneService userPhoneService;
|
||||
|
||||
/**
|
||||
* 发送短信验证码(需登录)
|
||||
* <p>
|
||||
* 当前手机号、未占用的新手机号均可发送;新号若已被其他账号占用则拒绝。
|
||||
*/
|
||||
@PostMapping("/send-code")
|
||||
@ApiOperationSupport(order = 1)
|
||||
@Operation(summary = "发送手机号变更验证码", description = "传入明文手机号,返回短信校验 id")
|
||||
public R sendCode(@Parameter(description = "手机号", required = true) @RequestParam String phone) {
|
||||
return userPhoneService.sendCode(phone);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验原手机号验证码(修改手机号第 1 步)
|
||||
*/
|
||||
@PostMapping("/verify-old")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "校验原手机号验证码", description = "传入发送验证码返回的 id 与验证码")
|
||||
public R verifyOld(@Valid @RequestBody PhoneVerifyDTO phoneVerify) {
|
||||
return R.status(userPhoneService.verifyOldPhone(phoneVerify));
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定新手机号(修改手机号第 3 步,需先完成 verify-old)
|
||||
*/
|
||||
@PostMapping("/change")
|
||||
@ApiOperationSupport(order = 3)
|
||||
@Operation(summary = "更换手机号", description = "传入新手机号及短信校验 id、验证码")
|
||||
public R change(@Valid @RequestBody PhoneChangeDTO phoneChange) {
|
||||
return R.status(userPhoneService.changePhone(phoneChange));
|
||||
}
|
||||
|
||||
}
|
||||
+25
-3
@@ -64,11 +64,11 @@ public interface UserConvert {
|
||||
@Mapping(target = "password", ignore = true)
|
||||
@Mapping(target = "birthday", ignore = true)
|
||||
@Mapping(target = "sex", ignore = true)
|
||||
@Mapping(target = "account", ignore = true)
|
||||
@Mapping(source = "workcode", target = "code")
|
||||
@Mapping(source = "lastname", target = "name")
|
||||
@Mapping(source = "lastname", target = "realName")
|
||||
@Mapping(source = "mobile", target = "phone")
|
||||
@Mapping(source = "mobile", target = "account")
|
||||
@Mapping(source = "email", target = "email")
|
||||
User baseConvert(OAPersonResponse person);
|
||||
|
||||
@@ -86,7 +86,7 @@ public interface UserConvert {
|
||||
*/
|
||||
default User person2user(OAPersonResponse person, String defaultPassword) {
|
||||
User user = baseConvert(person);
|
||||
|
||||
user.setAccount(resolveAccount(person));
|
||||
// 密码
|
||||
user.setPassword(defaultPassword);
|
||||
// 性别
|
||||
@@ -102,6 +102,28 @@ public interface UserConvert {
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析本系统登录账号:优先 OA loginid,其次工号,最后手机号
|
||||
*
|
||||
* @param person OA人员
|
||||
* @return 账号,无法识别时返回 null
|
||||
*/
|
||||
default String resolveAccount(OAPersonResponse person) {
|
||||
if (person == null) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isNotBlank(person.getLoginid())) {
|
||||
return person.getLoginid().trim();
|
||||
}
|
||||
if (StringUtils.isNotBlank(person.getWorkcode())) {
|
||||
return person.getWorkcode().trim();
|
||||
}
|
||||
if (StringUtils.isNotBlank(person.getMobile())) {
|
||||
return person.getMobile().trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* oa人员转本系统用户部门
|
||||
* @param person
|
||||
@@ -120,7 +142,7 @@ public interface UserConvert {
|
||||
// 排序
|
||||
userDept.setSort(OAUtils.parseInt(person.getDsporder()));
|
||||
// 用户id
|
||||
userDept.setUserId(userMap.get(person.getMobile()));
|
||||
userDept.setUserId(userMap.get(resolveAccount(person)));
|
||||
userDept.setSyncTime(new Date());
|
||||
return userDept;
|
||||
}
|
||||
|
||||
+2
-14
@@ -53,9 +53,6 @@ public class AirportMasterExcel implements Serializable {
|
||||
@ExcelIgnore
|
||||
private Long id;
|
||||
|
||||
@ExcelProperty("编码")
|
||||
private String code;
|
||||
|
||||
@ExcelProperty("IATA编码*")
|
||||
private String iataCode;
|
||||
|
||||
@@ -77,7 +74,7 @@ public class AirportMasterExcel implements Serializable {
|
||||
@ExcelProperty("所属区县*")
|
||||
private String districtName;
|
||||
|
||||
@ExcelProperty("详细地址")
|
||||
@ExcelProperty("详细地址*")
|
||||
private String detailAddress;
|
||||
|
||||
@ExcelProperty("经度*")
|
||||
@@ -88,19 +85,10 @@ public class AirportMasterExcel implements Serializable {
|
||||
@NumberFormat("0.000000")
|
||||
private BigDecimal latitude;
|
||||
|
||||
@ExcelProperty("行政区划编号")
|
||||
private String regionCode;
|
||||
|
||||
@ExcelProperty("数据来源")
|
||||
private String dataSource;
|
||||
|
||||
@ExcelProperty("启停状态")
|
||||
private String statusName;
|
||||
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty
|
||||
@ExcelIgnore
|
||||
private String errorMessage;
|
||||
|
||||
}
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.excel;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnore;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import cn.idev.excel.annotation.format.DateTimeFormat;
|
||||
import cn.idev.excel.annotation.format.NumberFormat;
|
||||
import cn.idev.excel.annotation.write.style.ColumnWidth;
|
||||
import cn.idev.excel.annotation.write.style.ContentRowHeight;
|
||||
import cn.idev.excel.annotation.write.style.HeadRowHeight;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 空港机场主数据导出 Excel
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@ColumnWidth(18)
|
||||
@HeadRowHeight(20)
|
||||
@ContentRowHeight(18)
|
||||
public class AirportMasterExportExcel implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ExcelIgnore
|
||||
private Long id;
|
||||
|
||||
@ExcelProperty("编码")
|
||||
private String code;
|
||||
|
||||
@ExcelProperty("IATA编码*")
|
||||
private String iataCode;
|
||||
|
||||
@ExcelProperty("ICAO代码*")
|
||||
private String icaoCode;
|
||||
|
||||
@ExcelProperty("机场标准名称*")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty("机场简称")
|
||||
private String shortName;
|
||||
|
||||
@ExcelProperty("所属省份*")
|
||||
private String provinceName;
|
||||
|
||||
@ExcelProperty("所属城市*")
|
||||
private String cityName;
|
||||
|
||||
@ExcelProperty("所属区县*")
|
||||
private String districtName;
|
||||
|
||||
@ExcelProperty("详细地址")
|
||||
private String detailAddress;
|
||||
|
||||
@ExcelProperty("经度*")
|
||||
@NumberFormat("0.000000")
|
||||
private BigDecimal longitude;
|
||||
|
||||
@ExcelProperty("纬度*")
|
||||
@NumberFormat("0.000000")
|
||||
private BigDecimal latitude;
|
||||
|
||||
@ExcelProperty("行政区划编号")
|
||||
private String regionCode;
|
||||
|
||||
@ExcelProperty("数据来源")
|
||||
private String dataSource;
|
||||
|
||||
@ExcelProperty("启停状态")
|
||||
private String statusName;
|
||||
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty("更新人")
|
||||
private String updateUserName;
|
||||
|
||||
@ExcelProperty("更新时间")
|
||||
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
@ExcelProperty("创建时间")
|
||||
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
@ExcelIgnore
|
||||
private String errorMessage;
|
||||
}
|
||||
+4
@@ -34,6 +34,7 @@ import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 费用项 Excel
|
||||
@@ -60,6 +61,9 @@ public class FeeItemExcel implements Serializable {
|
||||
@ExcelProperty("*费用项")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty("*税率")
|
||||
private BigDecimal taxRate;
|
||||
|
||||
@ExcelIgnore
|
||||
private String errorMessage;
|
||||
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* This file is part of the TMS extension for BladeX.
|
||||
*/
|
||||
package org.springblade.system.excel;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import cn.idev.excel.annotation.format.DateTimeFormat;
|
||||
import cn.idev.excel.annotation.write.style.ColumnWidth;
|
||||
import cn.idev.excel.annotation.write.style.ContentRowHeight;
|
||||
import cn.idev.excel.annotation.write.style.HeadRowHeight;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 费用项导出 Excel。
|
||||
*
|
||||
* <p>该模型仅用于导出,避免导出字段变更影响导入模板。</p>
|
||||
*/
|
||||
@Data
|
||||
@ColumnWidth(22)
|
||||
@HeadRowHeight(20)
|
||||
@ContentRowHeight(18)
|
||||
public class FeeItemExportExcel implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ExcelProperty("费用类型")
|
||||
private String feeCategory;
|
||||
|
||||
@ExcelProperty("费用项代码")
|
||||
private String englishName;
|
||||
|
||||
@ExcelProperty("费用项")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty("税率")
|
||||
private BigDecimal taxRate;
|
||||
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty("组织")
|
||||
private String createDeptName;
|
||||
|
||||
@ExcelProperty("更新人")
|
||||
private String updateUserName;
|
||||
|
||||
@ExcelProperty("更新时间")
|
||||
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
@ExcelProperty("创建时间")
|
||||
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
@ExcelProperty("状态")
|
||||
private String statusName;
|
||||
}
|
||||
+4
@@ -33,6 +33,7 @@ import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 费用项导入失败 Excel
|
||||
@@ -56,6 +57,9 @@ public class FeeItemImportFailureExcel implements Serializable {
|
||||
@ExcelProperty("*费用项")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty("*税率")
|
||||
private BigDecimal taxRate;
|
||||
|
||||
@ExcelProperty("导入失败原因")
|
||||
private String failureReason;
|
||||
|
||||
|
||||
+19
-22
@@ -53,54 +53,51 @@ public class PortTerminalExcel implements Serializable {
|
||||
@ExcelIgnore
|
||||
private Long id;
|
||||
|
||||
@ExcelProperty("编码")
|
||||
private String code;
|
||||
@ExcelProperty("港口编码*")
|
||||
private String portCode;
|
||||
|
||||
@ExcelProperty("港口/码头名称")
|
||||
@ExcelProperty("码头编码(如为港口则不需填写)")
|
||||
private String terminalCode;
|
||||
|
||||
@ExcelProperty("港口/码头名称*")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty("类型")
|
||||
@ExcelProperty("类型*")
|
||||
private String category;
|
||||
|
||||
@ExcelProperty("上级港口")
|
||||
@ExcelProperty("上级港口(如为港口则不需填写)")
|
||||
private String parentName;
|
||||
|
||||
@ExcelProperty("上级港口编码")
|
||||
@ExcelProperty("上级港口编码(如为港口则不需填写)")
|
||||
private String parentCode;
|
||||
|
||||
@ExcelProperty("国家")
|
||||
@ExcelProperty("国家*")
|
||||
private String country;
|
||||
|
||||
@ExcelProperty("城市")
|
||||
@ExcelProperty("所属省份*")
|
||||
private String provinceName;
|
||||
|
||||
@ExcelProperty("城市*")
|
||||
private String city;
|
||||
|
||||
@ExcelProperty("区县")
|
||||
@ExcelProperty("区县*")
|
||||
private String districtName;
|
||||
|
||||
@ExcelProperty("行政区划编码")
|
||||
private String regionCode;
|
||||
|
||||
@ExcelProperty("详细地址")
|
||||
@ExcelProperty("详细地址*")
|
||||
private String detailAddress;
|
||||
|
||||
@ExcelProperty("经度")
|
||||
@ExcelProperty("经度*")
|
||||
@NumberFormat("0.000000")
|
||||
private BigDecimal longitude;
|
||||
|
||||
@ExcelProperty("纬度")
|
||||
@ExcelProperty("纬度*")
|
||||
@NumberFormat("0.000000")
|
||||
private BigDecimal latitude;
|
||||
|
||||
@ExcelProperty("数据来源")
|
||||
private String dataSource;
|
||||
|
||||
@ExcelProperty("启停状态")
|
||||
private String statusName;
|
||||
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty
|
||||
@ExcelIgnore
|
||||
private String errorMessage;
|
||||
|
||||
}
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package org.springblade.system.excel;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnore;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import cn.idev.excel.annotation.format.DateTimeFormat;
|
||||
import cn.idev.excel.annotation.format.NumberFormat;
|
||||
import cn.idev.excel.annotation.write.style.ColumnWidth;
|
||||
import cn.idev.excel.annotation.write.style.ContentRowHeight;
|
||||
import cn.idev.excel.annotation.write.style.HeadRowHeight;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 港口码头主数据导出 Excel
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@ColumnWidth(18)
|
||||
@HeadRowHeight(20)
|
||||
@ContentRowHeight(18)
|
||||
public class PortTerminalExportExcel implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ExcelIgnore
|
||||
private Long id;
|
||||
|
||||
@ExcelProperty("编码")
|
||||
private String code;
|
||||
|
||||
@ExcelProperty("港口/码头名称")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty("类型")
|
||||
private String category;
|
||||
|
||||
@ExcelProperty("上级港口")
|
||||
private String parentName;
|
||||
|
||||
@ExcelProperty("上级港口编码")
|
||||
private String parentCode;
|
||||
|
||||
@ExcelProperty("国家")
|
||||
private String country;
|
||||
|
||||
@ExcelProperty("所属省份")
|
||||
private String provinceName;
|
||||
|
||||
@ExcelProperty("城市")
|
||||
private String city;
|
||||
|
||||
@ExcelProperty("区县")
|
||||
private String districtName;
|
||||
|
||||
@ExcelProperty("行政区划编码")
|
||||
private String regionCode;
|
||||
|
||||
@ExcelProperty("详细地址")
|
||||
private String detailAddress;
|
||||
|
||||
@ExcelProperty("经度")
|
||||
@NumberFormat("0.000000")
|
||||
private BigDecimal longitude;
|
||||
|
||||
@ExcelProperty("纬度")
|
||||
@NumberFormat("0.000000")
|
||||
private BigDecimal latitude;
|
||||
|
||||
@ExcelProperty("数据来源")
|
||||
private String dataSource;
|
||||
|
||||
@ExcelProperty("启停状态")
|
||||
private String statusName;
|
||||
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty("更新人")
|
||||
private String updateUserName;
|
||||
|
||||
@ExcelProperty("更新时间")
|
||||
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
@ExcelProperty("创建时间")
|
||||
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
@ExcelIgnore
|
||||
private String errorMessage;
|
||||
}
|
||||
+5
-17
@@ -51,13 +51,10 @@ public class RailwayStationExcel implements Serializable {
|
||||
@ExcelIgnore
|
||||
private Long id;
|
||||
|
||||
@ExcelProperty("编码")
|
||||
private String code;
|
||||
|
||||
@ExcelProperty("TMIS国标编码*")
|
||||
private String tmisCode;
|
||||
|
||||
@ExcelProperty("电报码*")
|
||||
@ExcelProperty("电报略码*")
|
||||
private String telegraphCode;
|
||||
|
||||
@ExcelProperty("车站名称*")
|
||||
@@ -75,28 +72,19 @@ public class RailwayStationExcel implements Serializable {
|
||||
@ExcelProperty("所属区县*")
|
||||
private String districtName;
|
||||
|
||||
@ExcelProperty("详细地址")
|
||||
@ExcelProperty("详细地址*")
|
||||
private String detailAddress;
|
||||
|
||||
@ExcelProperty("经度")
|
||||
@ExcelProperty("经度*")
|
||||
private String longitude;
|
||||
|
||||
@ExcelProperty("纬度")
|
||||
@ExcelProperty("纬度*")
|
||||
private String latitude;
|
||||
|
||||
@ExcelProperty("行政区划编号")
|
||||
private String regionCode;
|
||||
|
||||
@ExcelProperty("数据来源")
|
||||
private String dataSource;
|
||||
|
||||
@ExcelProperty("启停状态")
|
||||
private String statusName;
|
||||
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty
|
||||
@ExcelIgnore
|
||||
private String errorMessage;
|
||||
|
||||
}
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.excel;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnore;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import cn.idev.excel.annotation.format.DateTimeFormat;
|
||||
import cn.idev.excel.annotation.write.style.ColumnWidth;
|
||||
import cn.idev.excel.annotation.write.style.ContentRowHeight;
|
||||
import cn.idev.excel.annotation.write.style.HeadRowHeight;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 铁路车站主数据导出 Excel
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@ColumnWidth(18)
|
||||
@HeadRowHeight(20)
|
||||
@ContentRowHeight(18)
|
||||
public class RailwayStationExportExcel implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ExcelProperty("编码")
|
||||
private String code;
|
||||
|
||||
@ExcelProperty("TMIS国标编码*")
|
||||
private String tmisCode;
|
||||
|
||||
@ExcelProperty("电报略码*")
|
||||
private String telegraphCode;
|
||||
|
||||
@ExcelProperty("车站名称*")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty("所属省份*")
|
||||
private String provinceName;
|
||||
|
||||
@ExcelProperty("所属城市*")
|
||||
private String cityName;
|
||||
|
||||
@ExcelProperty("所属区县*")
|
||||
private String districtName;
|
||||
|
||||
@ExcelProperty("详细地址")
|
||||
private String detailAddress;
|
||||
|
||||
@ExcelProperty("经度*")
|
||||
private String longitude;
|
||||
|
||||
@ExcelProperty("纬度*")
|
||||
private String latitude;
|
||||
|
||||
@ExcelProperty("行政区划编号")
|
||||
private String regionCode;
|
||||
|
||||
@ExcelProperty("数据来源")
|
||||
private String dataSource;
|
||||
|
||||
@ExcelProperty("启停状态")
|
||||
private String statusName;
|
||||
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty("更新人")
|
||||
private String updateUserName;
|
||||
|
||||
@ExcelProperty("更新时间")
|
||||
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
@ExcelProperty("创建时间")
|
||||
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
@ExcelIgnore
|
||||
private Long id;
|
||||
}
|
||||
+5
-41
@@ -48,54 +48,18 @@ public class RegionExcel implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ExcelProperty("区划编号")
|
||||
@ExcelProperty("区域编码")
|
||||
private String code;
|
||||
|
||||
@ExcelProperty("父区划编号")
|
||||
private String parentCode;
|
||||
|
||||
@ExcelProperty("祖区划编号")
|
||||
private String ancestors;
|
||||
|
||||
@ExcelProperty("区划名称")
|
||||
@ExcelProperty("区域名称")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty("省级区划编号")
|
||||
private String provinceCode;
|
||||
@ExcelProperty("父级编码")
|
||||
private String parentCode;
|
||||
|
||||
@ExcelProperty("省级名称")
|
||||
private String provinceName;
|
||||
|
||||
@ExcelProperty("市级区划编号")
|
||||
private String cityCode;
|
||||
|
||||
@ExcelProperty("市级名称")
|
||||
private String cityName;
|
||||
|
||||
@ExcelProperty("区级区划编号")
|
||||
private String districtCode;
|
||||
|
||||
@ExcelProperty("区级名称")
|
||||
private String districtName;
|
||||
|
||||
@ExcelProperty("镇级区划编号")
|
||||
private String townCode;
|
||||
|
||||
@ExcelProperty("镇级名称")
|
||||
private String townName;
|
||||
|
||||
@ExcelProperty("村级区划编号")
|
||||
private String villageCode;
|
||||
|
||||
@ExcelProperty("村级名称")
|
||||
private String villageName;
|
||||
|
||||
@ExcelProperty("层级")
|
||||
@ExcelProperty("区域层级")
|
||||
private Integer regionLevel;
|
||||
|
||||
@ExcelProperty("排序")
|
||||
private Integer sort;
|
||||
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* This file is part of the TMS extension for BladeX.
|
||||
*/
|
||||
package org.springblade.system.excel;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import cn.idev.excel.annotation.format.DateTimeFormat;
|
||||
import cn.idev.excel.annotation.write.style.ColumnWidth;
|
||||
import cn.idev.excel.annotation.write.style.ContentRowHeight;
|
||||
import cn.idev.excel.annotation.write.style.HeadRowHeight;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 行政区划数据导出 Excel。
|
||||
*
|
||||
* <p>该模型仅用于导出,避免导入模板字段变更影响已有导入文件。</p>
|
||||
*/
|
||||
@Data
|
||||
@ColumnWidth(18)
|
||||
@HeadRowHeight(20)
|
||||
@ContentRowHeight(18)
|
||||
public class RegionExportExcel implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ExcelProperty("序号")
|
||||
private Integer serialNumber;
|
||||
|
||||
@ExcelProperty("区域编码")
|
||||
private String code;
|
||||
|
||||
@ExcelProperty("区域名称")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty("父级编码")
|
||||
private String parentCode;
|
||||
|
||||
@ExcelProperty("父级名称")
|
||||
private String parentName;
|
||||
|
||||
@ExcelProperty("区域层级")
|
||||
private Integer regionLevel;
|
||||
|
||||
@ExcelProperty("状态")
|
||||
private String statusName;
|
||||
|
||||
@ExcelProperty("数据来源")
|
||||
private String dataSource;
|
||||
|
||||
@ExcelProperty("更新人")
|
||||
private String updateUserName;
|
||||
|
||||
@ExcelProperty("更新时间")
|
||||
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
@ExcelProperty("创建时间")
|
||||
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
package org.springblade.system.feign;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.tenant.annotation.NonDS;
|
||||
import org.springblade.core.tool.api.R;
|
||||
@@ -63,6 +64,9 @@ public class SysClient implements ISysClient {
|
||||
|
||||
private final IRegionService regionService;
|
||||
|
||||
private final IFeeItemService feeItemService;
|
||||
private final ICargoTypeService cargoTypeService;
|
||||
|
||||
@Override
|
||||
@GetMapping(MENU)
|
||||
public R<Menu> getMenu(Long id) {
|
||||
@@ -163,6 +167,16 @@ public class SysClient implements ISysClient {
|
||||
return R.data(roleService.getRoleAliases(roleIds));
|
||||
}
|
||||
|
||||
@Override
|
||||
@GetMapping(ROLE_ID_BY_ALIAS)
|
||||
public R<String> getRoleIdByAlias(String tenantId, String roleAlias) {
|
||||
Role role = roleService.getOne(Wrappers.<Role>lambdaQuery()
|
||||
.eq(Role::getTenantId, tenantId)
|
||||
.eq(Role::getRoleAlias, roleAlias)
|
||||
.last("LIMIT 1"));
|
||||
return R.data(role == null || role.getId() == null ? null : String.valueOf(role.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@GetMapping(TENANT)
|
||||
public R<Tenant> getTenant(Long id) {
|
||||
@@ -200,5 +214,27 @@ public class SysClient implements ISysClient {
|
||||
return R.data(regionService.getById(code));
|
||||
}
|
||||
|
||||
@Override
|
||||
@GetMapping(FEE_ITEMS)
|
||||
public R<List<FeeItem>> getFeeItems() {
|
||||
return R.data(feeItemService.list(Wrappers.<FeeItem>lambdaQuery()
|
||||
.eq(FeeItem::getStatus, 1)
|
||||
.eq(FeeItem::getIsDeleted, 0)
|
||||
.orderByAsc(FeeItem::getFeeCategory, FeeItem::getName)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@GetMapping(CARGO_TYPES)
|
||||
public R<List<CargoType>> getCargoTypes() {
|
||||
return R.data(cargoTypeService.list(Wrappers.<CargoType>lambdaQuery()
|
||||
.eq(CargoType::getIsDeleted, 0)
|
||||
.orderByAsc(CargoType::getCargoCode)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@GetMapping(PERMISSIONS)
|
||||
public R<List<String>> getPermissions(String roleId) {
|
||||
return R.data(menuService.permissionCodes(roleId));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -108,6 +108,18 @@ public class UserClient implements IUserClient {
|
||||
return R.data(service.submit(user));
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping(UPDATE_USER)
|
||||
public R<Boolean> updateUser(@RequestBody User user) {
|
||||
return R.data(service.updateUser(user));
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping(SAVE_IAM_USER)
|
||||
public R<Boolean> saveIamUser(@RequestBody User user) {
|
||||
return R.data(service.saveIamUser(user));
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping(REGISTER_USER)
|
||||
public R<String> registerUser(User user) {
|
||||
@@ -121,4 +133,10 @@ public class UserClient implements IUserClient {
|
||||
return R.data(service.remove(Wrappers.<User>query().lambda().in(User::getTenantId, Func.toStrList(tenantIds))));
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping(BIND_WX_MINI_OPENID)
|
||||
public R<Boolean> bindWxMiniOpenId(String tenantId, Long userId, String openid, String phone) {
|
||||
return R.data(service.bindWxMiniOpenId(tenantId, userId, openid, phone));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+10
@@ -40,6 +40,16 @@ import java.util.List;
|
||||
*/
|
||||
public interface AirportMasterMapper extends BaseMapper<AirportMaster> {
|
||||
|
||||
/**
|
||||
* 按编码查询空港机场(包含逻辑删除记录,用于唯一性校验)。
|
||||
*/
|
||||
AirportMaster selectByCodeIncludingDeleted(@Param("code") String code);
|
||||
|
||||
/**
|
||||
* 恢复逻辑删除空港机场。
|
||||
*/
|
||||
int restoreById(@Param("id") Long id);
|
||||
|
||||
/**
|
||||
* 自定义分页
|
||||
*
|
||||
|
||||
+14
@@ -32,6 +32,20 @@
|
||||
<result column="remark" property="remark"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectByCodeIncludingDeleted" resultMap="airportMasterResultMap">
|
||||
SELECT
|
||||
am.*
|
||||
FROM blade_airport_master am
|
||||
WHERE am.code = #{code}
|
||||
</select>
|
||||
|
||||
<update id="restoreById">
|
||||
UPDATE blade_airport_master
|
||||
SET is_deleted = 0
|
||||
WHERE id = #{id}
|
||||
AND is_deleted = 1
|
||||
</update>
|
||||
|
||||
<select id="selectAirportMasterPage" resultMap="airportMasterResultMap">
|
||||
SELECT
|
||||
am.id,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<result column="pinyin_mnemonic" property="pinyinMnemonic"/>
|
||||
<result column="mnemonic_code" property="mnemonicCode"/>
|
||||
<result column="carrier_customer_id" property="carrierCustomerId"/>
|
||||
<result column="is_platform_company" property="isPlatformCompany"/>
|
||||
<result column="sort" property="sort"/>
|
||||
<result column="remark" property="remark"/>
|
||||
<result column="status" property="status"/>
|
||||
@@ -35,6 +36,7 @@
|
||||
<result column="pinyin_mnemonic" property="pinyinMnemonic"/>
|
||||
<result column="mnemonic_code" property="mnemonicCode"/>
|
||||
<result column="carrier_customer_id" property="carrierCustomerId"/>
|
||||
<result column="is_platform_company" property="isPlatformCompany"/>
|
||||
<result column="sort" property="sort"/>
|
||||
<result column="remark" property="remark"/>
|
||||
<result column="status" property="status"/>
|
||||
|
||||
+2
@@ -16,6 +16,8 @@
|
||||
<result column="fee_category" property="feeCategory"/>
|
||||
<result column="name" property="name"/>
|
||||
<result column="english_name" property="englishName"/>
|
||||
<result column="tax_rate" property="taxRate"/>
|
||||
<result column="remark" property="remark"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectFeeItemPage" resultMap="feeItemResultMap">
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments from this software for such purposes. Copyright of this software remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springblade.system.pojo.entity.InvoiceItem;
|
||||
import org.springblade.system.pojo.vo.InvoiceItemVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 开票项目 Mapper 接口
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface InvoiceItemMapper extends BaseMapper<InvoiceItem> {
|
||||
|
||||
List<InvoiceItemVO> selectInvoiceItemPage(IPage<InvoiceItemVO> page,
|
||||
@Param("invoiceItem") InvoiceItemVO invoiceItem);
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.springblade.system.mapper.InvoiceItemMapper">
|
||||
<resultMap id="invoiceItemResultMap" type="org.springblade.system.pojo.vo.InvoiceItemVO">
|
||||
<result column="id" property="id"/>
|
||||
<result column="create_user" property="createUser"/>
|
||||
<result column="create_dept" property="createDept"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_user" property="updateUser"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="is_deleted" property="isDeleted"/>
|
||||
<result column="create_dept_name" property="createDeptName"/>
|
||||
<result column="update_user_name" property="updateUserName"/>
|
||||
<result column="short_name" property="shortName"/>
|
||||
<result column="tax_classification_code" property="taxClassificationCode"/>
|
||||
<result column="category_name" property="categoryName"/>
|
||||
<result column="default_tax_rate" property="defaultTaxRate"/>
|
||||
</resultMap>
|
||||
<select id="selectInvoiceItemPage" resultMap="invoiceItemResultMap">
|
||||
SELECT bii.*, bd.dept_name AS create_dept_name, bu.real_name AS update_user_name
|
||||
FROM blade_invoice_item bii
|
||||
LEFT JOIN blade_dept bd ON bd.id = bii.create_dept
|
||||
LEFT JOIN blade_user bu ON bu.id = bii.update_user
|
||||
WHERE bii.is_deleted = 0
|
||||
<if test="invoiceItem.shortName != null and invoiceItem.shortName != ''">
|
||||
<bind name="shortNameLike" value="'%' + invoiceItem.shortName + '%'"/>
|
||||
AND bii.short_name LIKE #{shortNameLike}
|
||||
</if>
|
||||
<if test="invoiceItem.categoryName != null and invoiceItem.categoryName != ''">
|
||||
<bind name="categoryNameLike" value="'%' + invoiceItem.categoryName + '%'"/>
|
||||
AND bii.category_name LIKE #{categoryNameLike}
|
||||
</if>
|
||||
<if test="invoiceItem.taxClassificationCode != null and invoiceItem.taxClassificationCode != ''">
|
||||
<bind name="taxCodeLike" value="'%' + invoiceItem.taxClassificationCode + '%'"/>
|
||||
AND bii.tax_classification_code LIKE #{taxCodeLike}
|
||||
</if>
|
||||
ORDER BY bii.create_time DESC
|
||||
</select>
|
||||
</mapper>
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springblade.system.pojo.entity.MeasurementUnit;
|
||||
import org.springblade.system.pojo.vo.MeasurementUnitVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 计量单位 Mapper 接口
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface MeasurementUnitMapper extends BaseMapper<MeasurementUnit> {
|
||||
|
||||
/**
|
||||
* 自定义分页
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param measurementUnit 查询参数
|
||||
* @return 计量单位分页
|
||||
*/
|
||||
List<MeasurementUnitVO> selectMeasurementUnitPage(IPage<MeasurementUnitVO> page,
|
||||
@Param("measurementUnit") MeasurementUnitVO measurementUnit);
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.springblade.system.mapper.MeasurementUnitMapper">
|
||||
|
||||
<resultMap id="measurementUnitResultMap" type="org.springblade.system.pojo.vo.MeasurementUnitVO">
|
||||
<result column="id" property="id"/>
|
||||
<result column="create_user" property="createUser"/>
|
||||
<result column="create_user_name" property="createUserName"/>
|
||||
<result column="create_dept" property="createDept"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_user" property="updateUser"/>
|
||||
<result column="update_user_name" property="updateUserName"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="status" property="status"/>
|
||||
<result column="is_deleted" property="isDeleted"/>
|
||||
<result column="unit_code" property="unitCode"/>
|
||||
<result column="unit_name" property="unitName"/>
|
||||
<result column="dimension" property="dimension"/>
|
||||
<result column="remark" property="remark"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectMeasurementUnitPage" resultMap="measurementUnitResultMap">
|
||||
SELECT
|
||||
mmu.id,
|
||||
mmu.create_user,
|
||||
cu.real_name AS create_user_name,
|
||||
mmu.create_dept,
|
||||
mmu.create_time,
|
||||
mmu.update_user,
|
||||
uu.real_name AS update_user_name,
|
||||
mmu.update_time,
|
||||
mmu.status,
|
||||
mmu.is_deleted,
|
||||
mmu.unit_code,
|
||||
mmu.unit_name,
|
||||
mmu.dimension,
|
||||
mmu.remark
|
||||
FROM
|
||||
blade_measurement_unit mmu
|
||||
LEFT JOIN blade_user cu ON cu.id = mmu.create_user
|
||||
LEFT JOIN blade_user uu ON uu.id = mmu.update_user
|
||||
WHERE
|
||||
mmu.is_deleted = 0
|
||||
<if test="measurementUnit.unitCode != null and measurementUnit.unitCode != ''">
|
||||
<bind name="unitCodeLike" value="'%' + measurementUnit.unitCode + '%'"/>
|
||||
AND mmu.unit_code LIKE #{unitCodeLike}
|
||||
</if>
|
||||
<if test="measurementUnit.unitName != null and measurementUnit.unitName != ''">
|
||||
<bind name="unitNameLike" value="'%' + measurementUnit.unitName + '%'"/>
|
||||
AND mmu.unit_name LIKE #{unitNameLike}
|
||||
</if>
|
||||
<if test="measurementUnit.dimension != null and measurementUnit.dimension != ''">
|
||||
AND mmu.dimension = #{measurementUnit.dimension}
|
||||
</if>
|
||||
<if test="measurementUnit.status != null">
|
||||
AND mmu.status = #{measurementUnit.status}
|
||||
</if>
|
||||
ORDER BY mmu.create_time DESC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
+11
@@ -27,6 +27,7 @@ package org.springblade.system.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springblade.system.pojo.entity.PortTerminal;
|
||||
import org.springblade.system.pojo.vo.PortTerminalVO;
|
||||
|
||||
@@ -39,6 +40,16 @@ import java.util.List;
|
||||
*/
|
||||
public interface PortTerminalMapper extends BaseMapper<PortTerminal> {
|
||||
|
||||
/**
|
||||
* 按编码查询港口码头(包含逻辑删除记录,用于导入恢复)。
|
||||
*/
|
||||
PortTerminal selectByCodeIncludingDeleted(@Param("code") String code);
|
||||
|
||||
/**
|
||||
* 恢复逻辑删除港口码头。
|
||||
*/
|
||||
int restoreById(@Param("id") Long id);
|
||||
|
||||
/**
|
||||
* 自定义分页
|
||||
*
|
||||
|
||||
+33
-3
@@ -19,6 +19,8 @@
|
||||
<result column="parent_code" property="parentCode"/>
|
||||
<result column="parent_name" property="parentName"/>
|
||||
<result column="country" property="country"/>
|
||||
<result column="province_code" property="provinceCode"/>
|
||||
<result column="province_name" property="provinceName"/>
|
||||
<result column="city" property="city"/>
|
||||
<result column="district_code" property="districtCode"/>
|
||||
<result column="district_name" property="districtName"/>
|
||||
@@ -30,6 +32,19 @@
|
||||
<result column="remark" property="remark"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectByCodeIncludingDeleted" resultType="org.springblade.system.pojo.entity.PortTerminal">
|
||||
SELECT *
|
||||
FROM blade_port_terminal
|
||||
WHERE code = #{code}
|
||||
</select>
|
||||
|
||||
<update id="restoreById">
|
||||
UPDATE blade_port_terminal
|
||||
SET is_deleted = 0
|
||||
WHERE id = #{id}
|
||||
AND is_deleted = 1
|
||||
</update>
|
||||
|
||||
<select id="selectPortTerminalPage" resultMap="portTerminalResultMap">
|
||||
SELECT
|
||||
pt.id,
|
||||
@@ -48,13 +63,19 @@
|
||||
pt.parent_code,
|
||||
pt.parent_name,
|
||||
pt.country,
|
||||
pt.province_code,
|
||||
pt.province_name,
|
||||
pt.city,
|
||||
pt.district_code,
|
||||
pt.district_name,
|
||||
pt.detail_address,
|
||||
CASE WHEN pt.longitude BETWEEN -180 AND 180 THEN pt.longitude ELSE NULL END AS longitude,
|
||||
CASE WHEN pt.latitude BETWEEN -90 AND 90 THEN pt.latitude ELSE NULL END AS latitude,
|
||||
CASE WHEN pt.data_source = '初始导入' THEN '初始化导入' ELSE pt.data_source END AS data_source,
|
||||
CASE
|
||||
WHEN pt.data_source IN ('初始化导入', '初始导入') THEN '初始化录入'
|
||||
WHEN pt.data_source = '手工导入' THEN '手动录入'
|
||||
ELSE pt.data_source
|
||||
END AS data_source,
|
||||
pt.remark
|
||||
FROM
|
||||
blade_port_terminal pt
|
||||
@@ -77,8 +98,11 @@
|
||||
</if>
|
||||
<if test="portTerminal.dataSource != null and portTerminal.dataSource != ''">
|
||||
<choose>
|
||||
<when test="portTerminal.dataSource == '初始化导入'">
|
||||
AND pt.data_source IN ('初始化导入', '初始导入')
|
||||
<when test="portTerminal.dataSource == '初始化录入'">
|
||||
AND pt.data_source IN ('初始化录入', '初始化导入', '初始导入')
|
||||
</when>
|
||||
<when test="portTerminal.dataSource == '手动录入'">
|
||||
AND pt.data_source IN ('手动录入', '手工导入')
|
||||
</when>
|
||||
<otherwise>
|
||||
AND pt.data_source = #{portTerminal.dataSource}
|
||||
@@ -88,6 +112,12 @@
|
||||
<if test="portTerminal.city != null and portTerminal.city != ''">
|
||||
AND pt.city = #{portTerminal.city}
|
||||
</if>
|
||||
<if test="portTerminal.provinceName != null and portTerminal.provinceName != ''">
|
||||
AND pt.province_name = #{portTerminal.provinceName}
|
||||
</if>
|
||||
<if test="portTerminal.provinceCode != null and portTerminal.provinceCode != ''">
|
||||
AND pt.province_code = #{portTerminal.provinceCode}
|
||||
</if>
|
||||
<if test="portTerminal.districtCode != null and portTerminal.districtCode != ''">
|
||||
AND pt.district_code = #{portTerminal.districtCode}
|
||||
</if>
|
||||
|
||||
+10
@@ -40,6 +40,16 @@ import java.util.List;
|
||||
*/
|
||||
public interface RailwayStationMapper extends BaseMapper<RailwayStation> {
|
||||
|
||||
/**
|
||||
* 按编码查询铁路车站(包含逻辑删除记录,用于导入恢复)。
|
||||
*/
|
||||
RailwayStation selectByCodeIncludingDeleted(@Param("code") String code);
|
||||
|
||||
/**
|
||||
* 恢复逻辑删除铁路车站。
|
||||
*/
|
||||
int restoreById(@Param("id") Long id);
|
||||
|
||||
/**
|
||||
* 自定义分页
|
||||
*
|
||||
|
||||
+13
@@ -32,6 +32,19 @@
|
||||
<result column="remark" property="remark"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectByCodeIncludingDeleted" resultType="org.springblade.system.pojo.entity.RailwayStation">
|
||||
SELECT *
|
||||
FROM blade_railway_station
|
||||
WHERE code = #{code}
|
||||
</select>
|
||||
|
||||
<update id="restoreById">
|
||||
UPDATE blade_railway_station
|
||||
SET is_deleted = 0
|
||||
WHERE id = #{id}
|
||||
AND is_deleted = 1
|
||||
</update>
|
||||
|
||||
<select id="selectRailwayStationPage" resultMap="railwayStationResultMap">
|
||||
SELECT
|
||||
rs.id,
|
||||
|
||||
+2
-1
@@ -30,6 +30,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springblade.system.pojo.entity.Region;
|
||||
import org.springblade.system.excel.RegionExcel;
|
||||
import org.springblade.system.excel.RegionExportExcel;
|
||||
import org.springblade.system.pojo.vo.RegionVO;
|
||||
|
||||
import java.util.List;
|
||||
@@ -66,6 +67,6 @@ public interface RegionMapper extends BaseMapper<Region> {
|
||||
* @param queryWrapper
|
||||
* @return
|
||||
*/
|
||||
List<RegionExcel> exportRegion(@Param("ew") Wrapper<Region> queryWrapper);
|
||||
List<RegionExportExcel> exportRegion(@Param("ew") Wrapper<Region> queryWrapper);
|
||||
|
||||
}
|
||||
|
||||
+49
-13
@@ -18,7 +18,7 @@
|
||||
<result column="town_name" property="townName"/>
|
||||
<result column="village_code" property="villageCode"/>
|
||||
<result column="village_name" property="villageName"/>
|
||||
<result column="level" property="regionLevel"/>
|
||||
<result column="region_level" property="regionLevel"/>
|
||||
<result column="sort" property="sort"/>
|
||||
<result column="remark" property="remark"/>
|
||||
</resultMap>
|
||||
@@ -38,7 +38,7 @@
|
||||
<result column="town_name" property="townName"/>
|
||||
<result column="village_code" property="villageCode"/>
|
||||
<result column="village_name" property="villageName"/>
|
||||
<result column="level" property="regionLevel"/>
|
||||
<result column="region_level" property="regionLevel"/>
|
||||
<result column="sort" property="sort"/>
|
||||
<result column="remark" property="remark"/>
|
||||
<result column="id" property="id"/>
|
||||
@@ -72,7 +72,13 @@
|
||||
and region.name like concat(concat('%', #{param2.name}),'%')
|
||||
</if>
|
||||
<if test="param2.regionLevel!=null">
|
||||
and region.level = #{param2.regionLevel}
|
||||
and region.region_level = #{param2.regionLevel}
|
||||
</if>
|
||||
<if test="param2.dataSource!=null and param2.dataSource!=''">
|
||||
and region.data_source = #{param2.dataSource}
|
||||
</if>
|
||||
<if test="param2.status!=null and param2.status!=''">
|
||||
and region.status = #{param2.status}
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY region.sort ASC, region.code ASC
|
||||
@@ -92,21 +98,51 @@
|
||||
<if test="param1!=null">
|
||||
and region.parent_code = #{param1}
|
||||
</if>
|
||||
<if test="param2.code!=null and param2.code!=''">
|
||||
and region.code like concat(concat('%', #{param2.code}),'%')
|
||||
</if>
|
||||
<if test="param2.name!=null and param2.name!=''">
|
||||
and region.name like concat(concat('%', #{param2.name}),'%')
|
||||
</if>
|
||||
<if test="param2.regionLevel!=null">
|
||||
and region.level = #{param2.regionLevel}
|
||||
<if test="param2 != null and ((param2.code!=null and param2.code!='') or (param2.name!=null and param2.name!='') or param2.regionLevel!=null or (param2.dataSource!=null and param2.dataSource!='') or param2.status!=null)">
|
||||
and exists (
|
||||
select 1
|
||||
from blade_region matched
|
||||
where (matched.code = region.code or find_in_set(region.code, matched.ancestors) > 0)
|
||||
<if test="param2.code!=null and param2.code!=''">
|
||||
and matched.code like concat(concat('%', #{param2.code}),'%')
|
||||
</if>
|
||||
<if test="param2.name!=null and param2.name!=''">
|
||||
and matched.name like concat(concat('%', #{param2.name}),'%')
|
||||
</if>
|
||||
<if test="param2.regionLevel!=null">
|
||||
and matched.region_level = #{param2.regionLevel}
|
||||
</if>
|
||||
<if test="param2.dataSource!=null and param2.dataSource!=''">
|
||||
and matched.data_source = #{param2.dataSource}
|
||||
</if>
|
||||
<if test="param2.status!=null and param2.status!=''">
|
||||
and matched.status = #{param2.status}
|
||||
</if>
|
||||
)
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY region.sort ASC, region.code ASC
|
||||
</select>
|
||||
|
||||
<select id="exportRegion" resultType="org.springblade.system.excel.RegionExcel">
|
||||
SELECT * FROM blade_region ${ew.customSqlSegment}
|
||||
<select id="exportRegion" resultType="org.springblade.system.excel.RegionExportExcel">
|
||||
SELECT
|
||||
region.code,
|
||||
region.name,
|
||||
region.parent_code,
|
||||
CASE WHEN region.parent_code = '0' THEN '根节点'
|
||||
ELSE (SELECT parent.name FROM blade_region parent WHERE parent.code = region.parent_code)
|
||||
END AS parent_name,
|
||||
region.region_level,
|
||||
CASE region.status WHEN 1 THEN '启用' WHEN 2 THEN '停用' ELSE '' END AS status_name,
|
||||
region.data_source,
|
||||
COALESCE(updater.real_name, creator.real_name) AS update_user_name,
|
||||
region.update_time,
|
||||
region.create_time
|
||||
FROM blade_region region
|
||||
LEFT JOIN blade_user updater ON updater.id = region.update_user
|
||||
LEFT JOIN blade_user creator ON creator.id = region.create_user
|
||||
${ew.customSqlSegment}
|
||||
ORDER BY region.create_time DESC, region.code ASC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.props;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* IAM账号同步配置。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "iam.sync")
|
||||
public class IamSyncProperties {
|
||||
|
||||
/** IAM增量账号接口地址。 */
|
||||
private String accountListUrl;
|
||||
|
||||
/** IAM组织接口地址。 */
|
||||
private String orgListUrl;
|
||||
|
||||
/** IAM接口Authorization请求头。 */
|
||||
private String authorization;
|
||||
|
||||
/** IAM接口Auth请求头。 */
|
||||
private String profileAuthorization;
|
||||
|
||||
/** 单页请求数量。 */
|
||||
private int pageSize = 50;
|
||||
|
||||
}
|
||||
+2
-1
@@ -29,6 +29,7 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springblade.core.mp.base.BaseService;
|
||||
import org.springblade.system.excel.AirportMasterExcel;
|
||||
import org.springblade.system.excel.AirportMasterExportExcel;
|
||||
import org.springblade.system.pojo.entity.AirportMaster;
|
||||
import org.springblade.system.pojo.vo.AirportMasterVO;
|
||||
|
||||
@@ -80,6 +81,6 @@ public interface IAirportMasterService extends BaseService<AirportMaster> {
|
||||
* @param queryWrapper 查询条件
|
||||
* @return 导出数据
|
||||
*/
|
||||
List<AirportMasterExcel> exportAirportMaster(Wrapper<AirportMaster> queryWrapper);
|
||||
List<AirportMasterExportExcel> exportAirportMaster(Wrapper<AirportMaster> queryWrapper);
|
||||
|
||||
}
|
||||
|
||||
+14
@@ -97,6 +97,13 @@ public interface IDeptService extends IService<Dept> {
|
||||
*/
|
||||
List<Dept> selectDept(String deptId);
|
||||
|
||||
/**
|
||||
* 平台公司下拉(是否平台公司=是)
|
||||
*
|
||||
* @return 平台公司部门列表
|
||||
*/
|
||||
List<Dept> listPlatformCompany();
|
||||
|
||||
/**
|
||||
* 根据部门名称精确匹配获取部门ID集合
|
||||
*
|
||||
@@ -149,6 +156,13 @@ public interface IDeptService extends IService<Dept> {
|
||||
*/
|
||||
boolean submit(Dept dept);
|
||||
|
||||
/**
|
||||
* 从IAM同步管理租户组织。
|
||||
|
||||
* @return 同步处理的组织数量
|
||||
*/
|
||||
int syncIamOrganizations();
|
||||
|
||||
/**
|
||||
* 按名称与父级查询部门列表(限定当前会话租户)
|
||||
*
|
||||
|
||||
+2
-1
@@ -29,6 +29,7 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springblade.core.mp.base.BaseService;
|
||||
import org.springblade.system.excel.FeeItemExcel;
|
||||
import org.springblade.system.excel.FeeItemExportExcel;
|
||||
import org.springblade.system.excel.FeeItemImportFailureExcel;
|
||||
import org.springblade.system.pojo.entity.FeeItem;
|
||||
import org.springblade.system.pojo.vo.FeeItemVO;
|
||||
@@ -82,6 +83,6 @@ public interface IFeeItemService extends BaseService<FeeItem> {
|
||||
* @param queryWrapper 查询条件
|
||||
* @return 导出数据
|
||||
*/
|
||||
List<FeeItemExcel> exportFeeItem(Wrapper<FeeItem> queryWrapper);
|
||||
List<FeeItemExportExcel> exportFeeItem(Wrapper<FeeItem> queryWrapper);
|
||||
|
||||
}
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments from this software for such purposes. Copyright of this software remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springblade.core.mp.base.BaseService;
|
||||
import org.springblade.system.pojo.entity.InvoiceItem;
|
||||
import org.springblade.system.pojo.vo.InvoiceItemVO;
|
||||
|
||||
/**
|
||||
* 开票项目服务类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface IInvoiceItemService extends BaseService<InvoiceItem> {
|
||||
|
||||
IPage<InvoiceItemVO> selectInvoiceItemPage(IPage<InvoiceItemVO> page, InvoiceItemVO invoiceItem);
|
||||
|
||||
boolean submit(InvoiceItem invoiceItem);
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springblade.core.mp.base.BaseService;
|
||||
import org.springblade.system.pojo.entity.MeasurementUnit;
|
||||
import org.springblade.system.pojo.vo.MeasurementUnitVO;
|
||||
|
||||
/**
|
||||
* 计量单位服务类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface IMeasurementUnitService extends BaseService<MeasurementUnit> {
|
||||
|
||||
/**
|
||||
* 自定义分页
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param measurementUnit 查询参数
|
||||
* @return 计量单位分页
|
||||
*/
|
||||
IPage<MeasurementUnitVO> selectMeasurementUnitPage(IPage<MeasurementUnitVO> page,
|
||||
MeasurementUnitVO measurementUnit);
|
||||
|
||||
/**
|
||||
* 新增或修改计量单位
|
||||
*
|
||||
* @param measurementUnit 计量单位
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean submit(MeasurementUnit measurementUnit);
|
||||
|
||||
/**
|
||||
* 启用或停用计量单位
|
||||
*
|
||||
* @param id 主键
|
||||
* @param status 状态
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean changeStatus(Long id, Integer status);
|
||||
|
||||
}
|
||||
+8
@@ -77,6 +77,14 @@ public interface IMenuService extends IService<Menu> {
|
||||
*/
|
||||
List<MenuVO> buttons(String roleId);
|
||||
|
||||
/**
|
||||
* 权限标识集合(按钮编号,与前端 GetButtons 叶子 code 一致)
|
||||
*
|
||||
* @param roleId 角色id
|
||||
* @return 权限标识
|
||||
*/
|
||||
List<String> permissionCodes(String roleId);
|
||||
|
||||
/**
|
||||
* 树形结构
|
||||
*
|
||||
|
||||
+37
@@ -1,5 +1,8 @@
|
||||
package org.springblade.system.service;
|
||||
|
||||
import org.springblade.system.pojo.vo.OaOrgSyncPageVO;
|
||||
import org.springblade.system.pojo.vo.OaPersonSyncPageVO;
|
||||
|
||||
/**
|
||||
* oa同步接口
|
||||
* @author bfhuange
|
||||
@@ -18,4 +21,38 @@ public interface IOASyncService {
|
||||
* @param syncAll 是否同步所有
|
||||
*/
|
||||
void syncPersonAndPushMK(boolean syncAll);
|
||||
|
||||
/**
|
||||
* 从 OA 人员接口全量同步组织与人员,不推送 MK
|
||||
*
|
||||
* @return 处理的人员数量
|
||||
*/
|
||||
int syncPersonFromUserList();
|
||||
|
||||
/**
|
||||
* 按页从 OA 人员接口同步组织与人员
|
||||
*
|
||||
* @param current 当前页,从 1 开始
|
||||
* @param size 每页条数
|
||||
* @return 本页同步结果
|
||||
*/
|
||||
OaPersonSyncPageVO syncPersonFromUserList(int current, int size);
|
||||
|
||||
/**
|
||||
* 按页从 OA 公司接口同步公司
|
||||
*
|
||||
* @param current 当前页,从 1 开始
|
||||
* @param size 每页条数
|
||||
* @return 本页同步结果
|
||||
*/
|
||||
OaOrgSyncPageVO syncCompanyPage(int current, int size);
|
||||
|
||||
/**
|
||||
* 按页从 OA 部门接口同步部门;最后一页完成后更新祖级列表
|
||||
*
|
||||
* @param current 当前页,从 1 开始
|
||||
* @param size 每页条数
|
||||
* @return 本页同步结果
|
||||
*/
|
||||
OaOrgSyncPageVO syncDepartmentPage(int current, int size);
|
||||
}
|
||||
|
||||
+2
-1
@@ -29,6 +29,7 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springblade.core.mp.base.BaseService;
|
||||
import org.springblade.system.excel.PortTerminalExcel;
|
||||
import org.springblade.system.excel.PortTerminalExportExcel;
|
||||
import org.springblade.system.pojo.entity.PortTerminal;
|
||||
import org.springblade.system.pojo.vo.PortTerminalVO;
|
||||
|
||||
@@ -87,6 +88,6 @@ public interface IPortTerminalService extends BaseService<PortTerminal> {
|
||||
* @param queryWrapper 查询条件
|
||||
* @return 导出数据
|
||||
*/
|
||||
List<PortTerminalExcel> exportPortTerminal(Wrapper<PortTerminal> queryWrapper);
|
||||
List<PortTerminalExportExcel> exportPortTerminal(Wrapper<PortTerminal> queryWrapper);
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -29,6 +29,7 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springblade.core.mp.base.BaseService;
|
||||
import org.springblade.system.excel.RailwayStationExcel;
|
||||
import org.springblade.system.excel.RailwayStationExportExcel;
|
||||
import org.springblade.system.pojo.entity.RailwayStation;
|
||||
import org.springblade.system.pojo.vo.RailwayStationVO;
|
||||
|
||||
@@ -80,6 +81,6 @@ public interface IRailwayStationService extends BaseService<RailwayStation> {
|
||||
* @param queryWrapper 查询条件
|
||||
* @return 导出数据
|
||||
*/
|
||||
List<RailwayStationExcel> exportRailwayStation(Wrapper<RailwayStation> queryWrapper);
|
||||
List<RailwayStationExportExcel> exportRailwayStation(Wrapper<RailwayStation> queryWrapper);
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -29,6 +29,7 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springblade.system.pojo.entity.Region;
|
||||
import org.springblade.system.excel.RegionExcel;
|
||||
import org.springblade.system.excel.RegionExportExcel;
|
||||
import org.springblade.system.pojo.vo.RegionVO;
|
||||
|
||||
import java.util.List;
|
||||
@@ -90,6 +91,6 @@ public interface IRegionService extends IService<Region> {
|
||||
* @param queryWrapper
|
||||
* @return
|
||||
*/
|
||||
List<RegionExcel> exportRegion(Wrapper<Region> queryWrapper);
|
||||
List<RegionExportExcel> exportRegion(Wrapper<Region> queryWrapper);
|
||||
|
||||
}
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.service;
|
||||
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.system.pojo.dto.PhoneChangeDTO;
|
||||
import org.springblade.system.pojo.dto.PhoneVerifyDTO;
|
||||
|
||||
/**
|
||||
* 用户手机号变更服务
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface IUserPhoneService {
|
||||
|
||||
/**
|
||||
* 发送变更手机号短信验证码
|
||||
*
|
||||
* @param phone 明文手机号
|
||||
* @return 含短信校验 id 的响应
|
||||
*/
|
||||
R sendCode(String phone);
|
||||
|
||||
/**
|
||||
* 校验原手机号验证码,通过后写入短期凭证
|
||||
*
|
||||
* @param phoneVerify 校验参数
|
||||
* @return 是否通过
|
||||
*/
|
||||
boolean verifyOldPhone(PhoneVerifyDTO phoneVerify);
|
||||
|
||||
/**
|
||||
* 校验新手机号验证码并更换手机号
|
||||
*
|
||||
* @param phoneChange 更换参数
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean changePhone(PhoneChangeDTO phoneChange);
|
||||
|
||||
}
|
||||
+20
@@ -78,6 +78,13 @@ public interface IUserService extends BaseService<User> {
|
||||
*/
|
||||
boolean submit(User user);
|
||||
|
||||
/**
|
||||
* 从IAM同步管理租户账号。
|
||||
*
|
||||
* @return 同步处理的账号数量
|
||||
*/
|
||||
int syncIamAccounts();
|
||||
|
||||
/**
|
||||
* 修改用户(租户守卫校验用户归属,含账号 / 手机查重)
|
||||
*
|
||||
@@ -181,6 +188,11 @@ public interface IUserService extends BaseService<User> {
|
||||
*/
|
||||
UserInfo userInfo(UserOauth userOauth);
|
||||
|
||||
/**
|
||||
* 绑定微信小程序 openid 到已有用户(blade_user_oauth,source=WECHAT_MINI)
|
||||
*/
|
||||
boolean bindWxMiniOpenId(String tenantId, Long userId, String openid, String phone);
|
||||
|
||||
/**
|
||||
* 根据租户与账号获取用户
|
||||
*
|
||||
@@ -280,6 +292,14 @@ public interface IUserService extends BaseService<User> {
|
||||
*/
|
||||
boolean registerUser(User user);
|
||||
|
||||
/**
|
||||
* 新建或补齐IAM统一身份认证用户(按可信租户落库,默认分配角色 1123598816738675203)
|
||||
*
|
||||
* @param user 用户实体
|
||||
* @return 是否成功
|
||||
*/
|
||||
boolean saveIamUser(User user);
|
||||
|
||||
/**
|
||||
* 配置用户平台扩展信息(租户守卫校验用户归属)
|
||||
*
|
||||
|
||||
+216
-12
@@ -34,7 +34,9 @@ import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.system.excel.AirportMasterExcel;
|
||||
import org.springblade.system.excel.AirportMasterExportExcel;
|
||||
import org.springblade.system.mapper.AirportMasterMapper;
|
||||
import org.springblade.system.pojo.entity.AirportMaster;
|
||||
import org.springblade.system.pojo.entity.Region;
|
||||
@@ -43,12 +45,15 @@ import org.springblade.system.service.IAirportMasterService;
|
||||
import org.springblade.system.service.IRegionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.interceptor.TransactionAspectSupport;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -66,7 +71,7 @@ public class AirportMasterServiceImpl extends BaseServiceImpl<AirportMasterMappe
|
||||
private static final String SOURCE_BATCH = "批量导入";
|
||||
private static final String SOURCE_MANUAL = "手动录入";
|
||||
private static final String SOURCE_MANUAL_OLD = "手工导入";
|
||||
private static final String DEFAULT_COUNTRY_CODE = "+86";
|
||||
private static final int PROVINCE_REGION_LEVEL = 1;
|
||||
private static final int STATUS_ENABLED = 1;
|
||||
private static final int STATUS_DISABLED = 2;
|
||||
private static final int CODE_MAX_LENGTH = 20;
|
||||
@@ -95,6 +100,7 @@ public class AirportMasterServiceImpl extends BaseServiceImpl<AirportMasterMappe
|
||||
public boolean submit(AirportMaster airportMaster) {
|
||||
prepare(airportMaster, SOURCE_MANUAL);
|
||||
validate(airportMaster);
|
||||
prepareSubmitTarget(airportMaster);
|
||||
return saveOrUpdate(airportMaster);
|
||||
}
|
||||
|
||||
@@ -124,33 +130,199 @@ public class AirportMasterServiceImpl extends BaseServiceImpl<AirportMasterMappe
|
||||
throw new ServiceException("导入数据不能为空");
|
||||
}
|
||||
List<AirportMasterExcel> errorList = new ArrayList<>();
|
||||
List<AirportMaster> airportMasterList = new ArrayList<>();
|
||||
Map<String, Integer> iataCodeCountMap = buildImportValueCountMap(data.stream()
|
||||
.map(excel -> trimToEmpty(excel.getIataCode()).toUpperCase(Locale.ROOT))
|
||||
.toList());
|
||||
Map<String, Integer> icaoCodeCountMap = buildImportValueCountMap(data.stream()
|
||||
.map(excel -> trimToEmpty(excel.getIcaoCode()).toUpperCase(Locale.ROOT))
|
||||
.toList());
|
||||
for (int index = 0; index < data.size(); index++) {
|
||||
AirportMasterExcel excel = data.get(index);
|
||||
AirportMaster airportMaster = Objects.requireNonNull(BeanUtil.copyProperties(excel, AirportMaster.class));
|
||||
airportMaster.setDataSource(SOURCE_BATCH);
|
||||
airportMaster.setStatus(STATUS_ENABLED);
|
||||
normalizeImportAirportMaster(airportMaster);
|
||||
List<String> validationErrors = validateImportAirportMaster(airportMaster, iataCodeCountMap, icaoCodeCountMap);
|
||||
if (Func.isNotEmpty(validationErrors)) {
|
||||
excel.setErrorMessage(formatImportErrorMessage(validationErrors));
|
||||
errorList.add(excel);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
AirportMaster airportMaster = Objects.requireNonNull(BeanUtil.copyProperties(excel, AirportMaster.class));
|
||||
airportMaster.setDataSource(SOURCE_BATCH);
|
||||
airportMaster.setStatus(STATUS_ENABLED);
|
||||
prepare(airportMaster, SOURCE_BATCH);
|
||||
validate(airportMaster);
|
||||
save(airportMaster);
|
||||
airportMasterList.add(airportMaster);
|
||||
} catch (Exception exception) {
|
||||
String message = exception instanceof ServiceException ? exception.getMessage() : "导入失败";
|
||||
excel.setErrorMessage("第" + (index + 2) + "行:" + message);
|
||||
excel.setErrorMessage(formatImportErrorMessage(List.of(message)));
|
||||
errorList.add(excel);
|
||||
}
|
||||
}
|
||||
if (Func.isNotEmpty(errorList)) {
|
||||
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
|
||||
return errorList;
|
||||
}
|
||||
for (AirportMaster airportMaster : airportMasterList) {
|
||||
// 编码命中逻辑删除记录时复用原主键,恢复后更新,避免唯一索引冲突。
|
||||
prepareSubmitTarget(airportMaster);
|
||||
if (!saveOrUpdate(airportMaster)) {
|
||||
throw new ServiceException("空港机场保存失败");
|
||||
}
|
||||
}
|
||||
return errorList;
|
||||
}
|
||||
|
||||
private Map<String, Integer> buildImportValueCountMap(List<String> values) {
|
||||
Map<String, Integer> valueCountMap = new HashMap<>();
|
||||
for (String value : values) {
|
||||
if (Func.isNotEmpty(value)) {
|
||||
valueCountMap.merge(value, 1, Integer::sum);
|
||||
}
|
||||
}
|
||||
return valueCountMap;
|
||||
}
|
||||
|
||||
private void normalizeImportAirportMaster(AirportMaster airportMaster) {
|
||||
airportMaster.setIataCode(trimToEmpty(airportMaster.getIataCode()).toUpperCase(Locale.ROOT));
|
||||
airportMaster.setCode(CODE_PREFIX + airportMaster.getIataCode());
|
||||
airportMaster.setIcaoCode(normalizeOptionalCode(airportMaster.getIcaoCode()));
|
||||
airportMaster.setName(trimToEmpty(airportMaster.getName()));
|
||||
airportMaster.setShortName(trimToNull(airportMaster.getShortName()));
|
||||
airportMaster.setProvinceCode(trimToNull(airportMaster.getProvinceCode()));
|
||||
airportMaster.setProvinceName(trimToNull(airportMaster.getProvinceName()));
|
||||
airportMaster.setCityCode(trimToNull(airportMaster.getCityCode()));
|
||||
airportMaster.setCityName(trimToNull(airportMaster.getCityName()));
|
||||
airportMaster.setDistrictCode(trimToNull(airportMaster.getDistrictCode()));
|
||||
airportMaster.setDistrictName(trimToNull(airportMaster.getDistrictName()));
|
||||
airportMaster.setRegionCode(trimToNull(airportMaster.getRegionCode()));
|
||||
if (Func.isEmpty(airportMaster.getDistrictCode()) && Func.isNotEmpty(airportMaster.getRegionCode())) {
|
||||
airportMaster.setDistrictCode(airportMaster.getRegionCode());
|
||||
}
|
||||
airportMaster.setDetailAddress(trimToNull(airportMaster.getDetailAddress()));
|
||||
airportMaster.setRemark(trimToNull(airportMaster.getRemark()));
|
||||
}
|
||||
|
||||
private List<String> validateImportAirportMaster(AirportMaster airportMaster,
|
||||
Map<String, Integer> iataCodeCountMap, Map<String, Integer> icaoCodeCountMap) {
|
||||
List<String> validationErrors = new ArrayList<>();
|
||||
if (Func.isEmpty(airportMaster.getIataCode())) {
|
||||
addValidationError(validationErrors, "IATA编码不能为空");
|
||||
} else {
|
||||
if (!IATA_CODE_PATTERN.matcher(airportMaster.getIataCode()).matches()) {
|
||||
addValidationError(validationErrors, "IATA编码为3位大写字母");
|
||||
}
|
||||
if (iataCodeCountMap.getOrDefault(airportMaster.getIataCode(), 0) > 1) {
|
||||
addValidationError(validationErrors, "IATA编码在本次导入中重复");
|
||||
}
|
||||
validateImportUnique(AirportMaster::getIataCode, airportMaster.getIataCode(), "该IATA编码已存在", validationErrors);
|
||||
validateImportUnique(AirportMaster::getCode, airportMaster.getCode(), "该编码已存在", validationErrors);
|
||||
}
|
||||
if (Func.isEmpty(airportMaster.getIcaoCode())) {
|
||||
addValidationError(validationErrors, "ICAO代码不能为空");
|
||||
} else {
|
||||
if (!ICAO_CODE_PATTERN.matcher(airportMaster.getIcaoCode()).matches()) {
|
||||
addValidationError(validationErrors, "ICAO代码为4位大写字母");
|
||||
}
|
||||
if (icaoCodeCountMap.getOrDefault(airportMaster.getIcaoCode(), 0) > 1) {
|
||||
addValidationError(validationErrors, "ICAO代码在本次导入中重复");
|
||||
}
|
||||
validateImportUnique(AirportMaster::getIcaoCode, airportMaster.getIcaoCode(), "该ICAO代码已存在", validationErrors);
|
||||
}
|
||||
if (Func.isEmpty(airportMaster.getName())) {
|
||||
addValidationError(validationErrors, "机场标准名称不能为空");
|
||||
}
|
||||
validateImportLength(airportMaster.getCode(), CODE_MAX_LENGTH, "编码不能超过20字", validationErrors);
|
||||
validateImportLength(airportMaster.getName(), NAME_MAX_LENGTH, "机场标准名称不能超过100字", validationErrors);
|
||||
validateImportLength(airportMaster.getShortName(), SHORT_NAME_MAX_LENGTH, "机场简称不能超过100字", validationErrors);
|
||||
validateImportLength(airportMaster.getProvinceName(), REGION_NAME_MAX_LENGTH, "所属省份不能超过128字", validationErrors);
|
||||
validateImportLength(airportMaster.getCityName(), REGION_NAME_MAX_LENGTH, "所属城市不能超过128字", validationErrors);
|
||||
validateImportLength(airportMaster.getDistrictName(), REGION_NAME_MAX_LENGTH, "所属区县不能超过128字", validationErrors);
|
||||
validateImportLength(airportMaster.getRegionCode(), REGION_CODE_MAX_LENGTH, "行政区划编号不能超过32字", validationErrors);
|
||||
if (Func.isEmpty(airportMaster.getDetailAddress())) {
|
||||
addValidationError(validationErrors, "详细地址不能为空");
|
||||
}
|
||||
validateImportLength(airportMaster.getDetailAddress(), DETAIL_ADDRESS_MAX_LENGTH, "详细地址不能超过255字", validationErrors);
|
||||
validateImportLength(airportMaster.getRemark(), REMARK_MAX_LENGTH, "备注不能超过200字", validationErrors);
|
||||
if (Func.isEmpty(airportMaster.getLongitude())) {
|
||||
addValidationError(validationErrors, "经度不能为空");
|
||||
} else if (!validRange(airportMaster.getLongitude(), MIN_LONGITUDE, MAX_LONGITUDE)) {
|
||||
addValidationError(validationErrors, "经度范围为 -180 到 180");
|
||||
}
|
||||
if (Func.isEmpty(airportMaster.getLatitude())) {
|
||||
addValidationError(validationErrors, "纬度不能为空");
|
||||
} else if (!validRange(airportMaster.getLatitude(), MIN_LATITUDE, MAX_LATITUDE)) {
|
||||
addValidationError(validationErrors, "纬度范围为 -90 到 90");
|
||||
}
|
||||
validateImportAirportRegion(airportMaster, validationErrors);
|
||||
return validationErrors;
|
||||
}
|
||||
|
||||
private void validateImportAirportRegion(AirportMaster airportMaster, List<String> validationErrors) {
|
||||
boolean provinceMissing = Func.isEmpty(airportMaster.getProvinceCode()) && Func.isEmpty(airportMaster.getProvinceName());
|
||||
boolean cityMissing = Func.isEmpty(airportMaster.getCityCode()) && Func.isEmpty(airportMaster.getCityName());
|
||||
boolean districtMissing = Func.isEmpty(airportMaster.getDistrictCode()) && Func.isEmpty(airportMaster.getDistrictName());
|
||||
if (provinceMissing) {
|
||||
addValidationError(validationErrors, "所属省份不能为空");
|
||||
}
|
||||
if (cityMissing) {
|
||||
addValidationError(validationErrors, "所属城市不能为空");
|
||||
}
|
||||
if (districtMissing) {
|
||||
addValidationError(validationErrors, "所属区县不能为空");
|
||||
}
|
||||
if (provinceMissing || cityMissing || districtMissing) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
fillRegion(airportMaster);
|
||||
} catch (ServiceException exception) {
|
||||
addValidationError(validationErrors, exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void validateImportUnique(com.baomidou.mybatisplus.core.toolkit.support.SFunction<AirportMaster, ?> column,
|
||||
String value, String message, List<String> validationErrors) {
|
||||
if (count(Wrappers.<AirportMaster>lambdaQuery()
|
||||
.eq(column, value)
|
||||
.eq(AirportMaster::getIsDeleted, 0)) > 0L) {
|
||||
addValidationError(validationErrors, message);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateImportLength(String value, int maxLength, String message, List<String> validationErrors) {
|
||||
if (Func.isNotEmpty(value) && value.length() > maxLength) {
|
||||
addValidationError(validationErrors, message);
|
||||
}
|
||||
}
|
||||
|
||||
private void addValidationError(List<String> validationErrors, String message) {
|
||||
if (Func.isNotEmpty(message) && !validationErrors.contains(message)) {
|
||||
validationErrors.add(message);
|
||||
}
|
||||
}
|
||||
|
||||
private String formatImportErrorMessage(List<String> validationErrors) {
|
||||
StringBuilder errorMessage = new StringBuilder();
|
||||
for (int index = 0; index < validationErrors.size(); index++) {
|
||||
if (index > 0) {
|
||||
errorMessage.append(System.lineSeparator());
|
||||
}
|
||||
errorMessage.append(index + 1).append(". ").append(validationErrors.get(index));
|
||||
}
|
||||
return errorMessage.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AirportMasterExcel> exportAirportMaster(Wrapper<AirportMaster> queryWrapper) {
|
||||
public List<AirportMasterExportExcel> exportAirportMaster(Wrapper<AirportMaster> queryWrapper) {
|
||||
List<AirportMaster> airportMasterList = list(queryWrapper);
|
||||
return airportMasterList.stream().map(airportMaster -> {
|
||||
AirportMasterExcel excel = Objects.requireNonNull(BeanUtil.copyProperties(airportMaster, AirportMasterExcel.class));
|
||||
AirportMasterExportExcel excel = Objects.requireNonNull(BeanUtil.copyProperties(airportMaster, AirportMasterExportExcel.class));
|
||||
excel.setLongitude(scaleCoordinate(airportMaster.getLongitude(), MIN_LONGITUDE, MAX_LONGITUDE));
|
||||
excel.setLatitude(scaleCoordinate(airportMaster.getLatitude(), MIN_LATITUDE, MAX_LATITUDE));
|
||||
excel.setDataSource(normalizeDataSource(airportMaster.getDataSource()));
|
||||
excel.setStatusName(Objects.equals(airportMaster.getStatus(), STATUS_ENABLED) ? "启用" : "停用");
|
||||
excel.setUpdateUserName(UserCache.getUserRealName(airportMaster.getUpdateUser()));
|
||||
return excel;
|
||||
}).toList();
|
||||
}
|
||||
@@ -165,7 +337,7 @@ public class AirportMasterServiceImpl extends BaseServiceImpl<AirportMasterMappe
|
||||
private void prepare(AirportMaster airportMaster, String defaultDataSource) {
|
||||
airportMaster.setIataCode(trimToEmpty(airportMaster.getIataCode()).toUpperCase(Locale.ROOT));
|
||||
airportMaster.setCode(CODE_PREFIX + airportMaster.getIataCode());
|
||||
airportMaster.setIcaoCode(trimToEmpty(airportMaster.getIcaoCode()).toUpperCase(Locale.ROOT));
|
||||
airportMaster.setIcaoCode(normalizeOptionalCode(airportMaster.getIcaoCode()));
|
||||
airportMaster.setName(trimToEmpty(airportMaster.getName()));
|
||||
airportMaster.setShortName(trimToNull(airportMaster.getShortName()));
|
||||
airportMaster.setProvinceCode(trimToNull(airportMaster.getProvinceCode()));
|
||||
@@ -220,6 +392,9 @@ public class AirportMasterServiceImpl extends BaseServiceImpl<AirportMasterMappe
|
||||
if (Func.isEmpty(airportMaster.getProvinceCode()) || Func.isEmpty(airportMaster.getCityCode()) || Func.isEmpty(airportMaster.getDistrictCode())) {
|
||||
throw new ServiceException("请选择省份、城市和区县");
|
||||
}
|
||||
if (Func.isEmpty(airportMaster.getDetailAddress())) {
|
||||
throw new ServiceException("详细地址不能为空");
|
||||
}
|
||||
validateCoordinate(airportMaster.getLongitude(), MIN_LONGITUDE, MAX_LONGITUDE, "经度");
|
||||
validateCoordinate(airportMaster.getLatitude(), MIN_LATITUDE, MAX_LATITUDE, "纬度");
|
||||
validateDataSource(airportMaster.getDataSource());
|
||||
@@ -235,9 +410,10 @@ public class AirportMasterServiceImpl extends BaseServiceImpl<AirportMasterMappe
|
||||
province = regionService.getById(airportMaster.getProvinceCode());
|
||||
}
|
||||
if (Func.isEmpty(province) && Func.isNotEmpty(airportMaster.getProvinceName())) {
|
||||
String provinceName = normalizeRegionName(airportMaster.getProvinceName());
|
||||
province = regionService.getOne(Wrappers.<Region>lambdaQuery()
|
||||
.eq(Region::getParentCode, DEFAULT_COUNTRY_CODE)
|
||||
.eq(Region::getName, airportMaster.getProvinceName()), false);
|
||||
.eq(Region::getRegionLevel, PROVINCE_REGION_LEVEL)
|
||||
.eq(Region::getName, provinceName), false);
|
||||
}
|
||||
if (Func.isEmpty(province)) {
|
||||
throw new ServiceException("请选择省份");
|
||||
@@ -315,7 +491,26 @@ public class AirportMasterServiceImpl extends BaseServiceImpl<AirportMasterMappe
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareSubmitTarget(AirportMaster airportMaster) {
|
||||
if (Func.isNotEmpty(airportMaster.getId())) {
|
||||
return;
|
||||
}
|
||||
AirportMaster existingAirportMaster = baseMapper.selectByCodeIncludingDeleted(airportMaster.getCode());
|
||||
if (existingAirportMaster == null) {
|
||||
return;
|
||||
}
|
||||
if (!Objects.equals(existingAirportMaster.getIsDeleted(), 1)) {
|
||||
throw new ServiceException("该编码已存在");
|
||||
}
|
||||
baseMapper.restoreById(existingAirportMaster.getId());
|
||||
airportMaster.setId(existingAirportMaster.getId());
|
||||
airportMaster.setIsDeleted(0);
|
||||
}
|
||||
|
||||
private void validateUnique(AirportMaster airportMaster, com.baomidou.mybatisplus.core.toolkit.support.SFunction<AirportMaster, ?> column, String value, String message) {
|
||||
if (Func.isEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
LambdaQueryWrapper<AirportMaster> queryWrapper = Wrappers.<AirportMaster>lambdaQuery()
|
||||
.eq(column, value)
|
||||
.eq(AirportMaster::getIsDeleted, 0);
|
||||
@@ -328,7 +523,11 @@ public class AirportMasterServiceImpl extends BaseServiceImpl<AirportMasterMappe
|
||||
}
|
||||
|
||||
private String trimToEmpty(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
return value == null ? "" : value.replace("\uFEFF", "").strip();
|
||||
}
|
||||
|
||||
private String normalizeRegionName(String value) {
|
||||
return trimToEmpty(value);
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
@@ -336,6 +535,11 @@ public class AirportMasterServiceImpl extends BaseServiceImpl<AirportMasterMappe
|
||||
return trimValue.isEmpty() ? null : trimValue;
|
||||
}
|
||||
|
||||
private String normalizeOptionalCode(String value) {
|
||||
String normalizedValue = trimToNull(value);
|
||||
return normalizedValue == null ? null : normalizedValue.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private String normalizeDataSource(String dataSource) {
|
||||
String value = trimToEmpty(dataSource);
|
||||
return SOURCE_MANUAL_OLD.equals(value) ? SOURCE_MANUAL : value;
|
||||
|
||||
+1
-1
@@ -149,7 +149,7 @@ public class CargoTypeServiceImpl extends BaseServiceImpl<CargoTypeMapper, Cargo
|
||||
} catch (Exception exception) {
|
||||
String message = exception instanceof ServiceException ? exception.getMessage() : "导入失败";
|
||||
excel.setErrorMessage(message);
|
||||
failureList.add(toImportFailureExcel(excel, "第" + (index + 2) + "行:" + message));
|
||||
failureList.add(toImportFailureExcel(excel, message));
|
||||
}
|
||||
}
|
||||
return failureList;
|
||||
|
||||
+1
-1
@@ -134,7 +134,7 @@ public class CurrencyServiceImpl extends BaseServiceImpl<CurrencyMapper, Currenc
|
||||
successCount++;
|
||||
} catch (Exception exception) {
|
||||
String message = exception instanceof ServiceException ? exception.getMessage() : "导入失败";
|
||||
excel.setErrorMessage("第" + (index + 2) + "行:" + message);
|
||||
excel.setErrorMessage(message);
|
||||
errorList.add(excel);
|
||||
}
|
||||
}
|
||||
|
||||
+213
-1
@@ -29,8 +29,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.common.constant.DataStatusEnum;
|
||||
import org.springblade.core.cache.utils.CacheUtil;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
@@ -44,6 +48,7 @@ import org.springblade.system.pojo.entity.Dept;
|
||||
import org.springblade.system.pojo.entity.User;
|
||||
import org.springblade.system.pojo.vo.DeptVO;
|
||||
import org.springblade.system.pojo.vo.UserVO;
|
||||
import org.springblade.system.props.IamSyncProperties;
|
||||
import org.springblade.system.service.IDeptService;
|
||||
import org.springblade.system.service.IUserService;
|
||||
import org.springblade.system.wrapper.DeptWrapper;
|
||||
@@ -52,6 +57,13 @@ import org.springblade.thirdparty.oa.constant.OAConvertConstant;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -59,6 +71,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import static org.springblade.core.tenant.TenantGuard.EntityType.DEPT;
|
||||
import static org.springblade.core.tenant.TenantGuard.EntityType.DEPT_PARENT;
|
||||
import static org.springblade.core.cache.constant.CacheConstant.SYS_CACHE;
|
||||
|
||||
/**
|
||||
* 服务实现类
|
||||
@@ -72,8 +85,13 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
|
||||
private static final String TENANT_ID = "tenantId";
|
||||
private static final String PARENT_ID = "parentId";
|
||||
private static final String IAM_SYNC_TENANT_ID = "000000";
|
||||
private static final Long IAM_SYNC_PARENT_ID = 1123598813738675201L;
|
||||
private static final HttpClient IAM_HTTP_CLIENT = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
|
||||
|
||||
private final IUserService userService;
|
||||
private final IamSyncProperties iamSyncProperties;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public Dept getDetail(Dept dept) {
|
||||
@@ -151,6 +169,18 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
return baseMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Dept> listPlatformCompany() {
|
||||
LambdaQueryWrapper<Dept> queryWrapper = Wrappers.<Dept>lambdaQuery()
|
||||
.eq(Dept::getIsPlatformCompany, 1)
|
||||
.orderByAsc(Dept::getSort)
|
||||
.orderByAsc(Dept::getId);
|
||||
if (!AuthUtil.isAdministrator()) {
|
||||
queryWrapper.eq(Dept::getTenantId, AuthUtil.getTenantId());
|
||||
}
|
||||
return list(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeptIds(String tenantId, String deptNames) {
|
||||
List<Dept> deptList = baseMapper.selectList(Wrappers.<Dept>query().lambda().eq(Dept::getTenantId, tenantId).in(Dept::getDeptName, Func.toStrList(deptNames)));
|
||||
@@ -232,6 +262,9 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
dept.setAncestors(parent.getAncestors() + StringPool.COMMA + dept.getParentId());
|
||||
}
|
||||
dept.setIsDeleted(BladeConstant.DB_NOT_DELETED);
|
||||
if (dept.getIsPlatformCompany() == null) {
|
||||
dept.setIsPlatformCompany(0);
|
||||
}
|
||||
if (Func.isEmpty(dept.getTenantId())) {
|
||||
throw new ServiceException("租户ID不能为空");
|
||||
}
|
||||
@@ -240,6 +273,183 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
return saveOrUpdate(dept);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int syncIamOrganizations() {
|
||||
int pageNumber = 1;
|
||||
int fetchedCount = 0;
|
||||
int syncedCount = 0;
|
||||
int totalCount = -1;
|
||||
int pageSize = iamSyncProperties.getPageSize() > 0 ? iamSyncProperties.getPageSize() : 50;
|
||||
while (true) {
|
||||
JsonNode dataNode = requestIamOrgPage(pageNumber, pageSize);
|
||||
JsonNode orgList = dataNode.path("list");
|
||||
if (!orgList.isArray() || orgList.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
if (dataNode.has("total")) {
|
||||
totalCount = dataNode.path("total").asInt(totalCount);
|
||||
}
|
||||
for (JsonNode orgNode : orgList) {
|
||||
if (syncIamOrganization(orgNode)) {
|
||||
syncedCount++;
|
||||
}
|
||||
}
|
||||
fetchedCount += orgList.size();
|
||||
int responsePage = dataNode.path("page").asInt(pageNumber);
|
||||
int responseSize = dataNode.path("size").asInt(pageSize);
|
||||
if ((totalCount >= 0 && fetchedCount >= totalCount)
|
||||
|| orgList.size() < pageSize
|
||||
|| (totalCount >= 0 && responsePage * responseSize >= totalCount)) {
|
||||
break;
|
||||
}
|
||||
pageNumber = responsePage + 1;
|
||||
}
|
||||
log.info("IAM组织同步完成,tenantId={}, fetchedCount={}, syncedCount={}", IAM_SYNC_TENANT_ID, fetchedCount, syncedCount);
|
||||
return syncedCount;
|
||||
}
|
||||
|
||||
private JsonNode requestIamOrgPage(int pageNumber, int pageSize) {
|
||||
try {
|
||||
Map<String, String> requestBody = new LinkedHashMap<>();
|
||||
requestBody.put("size", String.valueOf(pageSize));
|
||||
requestBody.put("page", String.valueOf(pageNumber));
|
||||
HttpRequest request = HttpRequest.newBuilder(URI.create(iamSyncProperties.getOrgListUrl()))
|
||||
.timeout(Duration.ofSeconds(20))
|
||||
.header("Accept", "application/json")
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Auth", normalizeAuthorizationHeader(iamSyncProperties.getProfileAuthorization()))
|
||||
.header("Authorization", normalizeAuthorizationHeader(iamSyncProperties.getAuthorization()))
|
||||
.POST(HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(requestBody), StandardCharsets.UTF_8))
|
||||
.build();
|
||||
HttpResponse<String> response = IAM_HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||
if (response.statusCode() < 200 || response.statusCode() >= 300) {
|
||||
throw new ServiceException(StringUtil.format("IAM组织接口调用失败,HTTP状态码:{}", response.statusCode()));
|
||||
}
|
||||
JsonNode responseNode = objectMapper.readTree(response.body());
|
||||
if (!"0".equals(responseNode.path("code").asText())) {
|
||||
throw new ServiceException(StringUtil.format("IAM组织接口调用失败:{}", responseNode.path("msg").asText()));
|
||||
}
|
||||
JsonNode dataNode = responseNode.path("data");
|
||||
if (!dataNode.isObject()) {
|
||||
throw new ServiceException("IAM组织接口返回数据格式错误");
|
||||
}
|
||||
return dataNode;
|
||||
} catch (InterruptedException exception) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.error("调用IAM组织接口被中断,page={}", pageNumber, exception);
|
||||
throw new ServiceException("调用IAM组织接口被中断");
|
||||
} catch (IOException | IllegalArgumentException exception) {
|
||||
log.error("调用IAM组织接口失败,page={}", pageNumber, exception);
|
||||
throw new ServiceException("调用IAM组织接口失败");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean syncIamOrganization(JsonNode orgNode) {
|
||||
String orgCode = readIamText(orgNode, "orgCode", "org_code", "organizationCode", "organization_code",
|
||||
"code", "app_org__org_code", "app_org__org_no", "app_org__organization_code", "app_org__code");
|
||||
String orgId = readIamText(orgNode, "orgId", "org_id", "id", "app_org__id", "app_org__org_id");
|
||||
if (StringUtil.isBlank(orgCode)) {
|
||||
orgCode = orgId;
|
||||
}
|
||||
if (StringUtil.isBlank(orgCode)) {
|
||||
log.warn("IAM组织缺少组织编码,跳过同步");
|
||||
return false;
|
||||
}
|
||||
if (orgCode.length() > 30) {
|
||||
log.warn("IAM组织编码超过30个字符,跳过同步,orgCode={}", orgCode);
|
||||
return false;
|
||||
}
|
||||
String orgName = readIamText(orgNode, "name", "orgName", "org_name", "organizationName", "organization_name",
|
||||
"fullName", "app_org__name", "app_org__org_name", "app_org__org_full_name", "app_org__organization_name");
|
||||
if (StringUtil.isBlank(orgName)) {
|
||||
log.warn("IAM组织缺少组织名称,跳过同步,orgCode={}", orgCode);
|
||||
return false;
|
||||
}
|
||||
Integer status = readIamInt(orgNode, "status", "org_status", "app_org__status", "app_org__org_status") == 1
|
||||
? DataStatusEnum.ENABLE.getCode() : DataStatusEnum.DISABLE.getCode();
|
||||
Dept dept = getOne(Wrappers.<Dept>lambdaQuery()
|
||||
.eq(Dept::getTenantId, IAM_SYNC_TENANT_ID)
|
||||
.eq(Dept::getDeptCode, orgCode), false);
|
||||
if (dept == null) {
|
||||
dept = new Dept();
|
||||
dept.setTenantId(IAM_SYNC_TENANT_ID);
|
||||
dept.setParentId(IAM_SYNC_PARENT_ID);
|
||||
dept.setAncestors(resolveIamParentAncestors());
|
||||
dept.setDeptCode(orgCode);
|
||||
dept.setDeptName(orgName);
|
||||
dept.setFullName(orgName);
|
||||
dept.setShortName(orgName);
|
||||
dept.setDeptCategory(1);
|
||||
dept.setSort(0);
|
||||
dept.setStatus(status);
|
||||
dept.setIsDeleted(BladeConstant.DB_NOT_DELETED);
|
||||
dept.setIsOa(1);
|
||||
dept.setIsPlatformCompany(0);
|
||||
dept.setSyncTime(new Date());
|
||||
boolean saved = save(dept);
|
||||
if (saved) {
|
||||
CacheUtil.clear(SYS_CACHE);
|
||||
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
boolean changed = !Objects.equals(dept.getDeptName(), orgName) || !Objects.equals(dept.getFullName(), orgName)
|
||||
|| !Objects.equals(dept.getShortName(), orgName) || !Objects.equals(dept.getStatus(), status)
|
||||
|| !Objects.equals(dept.getIsOa(), 1);
|
||||
if (!changed) {
|
||||
return true;
|
||||
}
|
||||
dept.setDeptName(orgName);
|
||||
dept.setFullName(orgName);
|
||||
dept.setShortName(orgName);
|
||||
dept.setStatus(status);
|
||||
dept.setIsOa(1);
|
||||
dept.setSyncTime(new Date());
|
||||
CacheUtil.clear(SYS_CACHE);
|
||||
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
|
||||
return updateById(dept);
|
||||
}
|
||||
|
||||
private String resolveIamParentAncestors() {
|
||||
Dept parent = getById(IAM_SYNC_PARENT_ID);
|
||||
String ancestors = parent == null ? String.valueOf(BladeConstant.TOP_PARENT_ID) : parent.getAncestors();
|
||||
if (StringUtil.isBlank(ancestors)) {
|
||||
ancestors = String.valueOf(BladeConstant.TOP_PARENT_ID);
|
||||
}
|
||||
return ancestors + StringPool.COMMA + IAM_SYNC_PARENT_ID;
|
||||
}
|
||||
|
||||
private String readIamText(JsonNode node, String... fieldNames) {
|
||||
JsonNode valueNode = findIamNode(node, fieldNames);
|
||||
return valueNode == null || valueNode.isNull() ? StringPool.EMPTY : valueNode.asText().trim();
|
||||
}
|
||||
|
||||
private int readIamInt(JsonNode node, String... fieldNames) {
|
||||
JsonNode valueNode = findIamNode(node, fieldNames);
|
||||
return valueNode == null || valueNode.isNull() ? 0 : valueNode.asInt(0);
|
||||
}
|
||||
|
||||
private JsonNode findIamNode(JsonNode node, String... fieldNames) {
|
||||
for (String fieldName : fieldNames) {
|
||||
JsonNode valueNode = node.get(fieldName);
|
||||
if (valueNode != null && !valueNode.isNull()) {
|
||||
return valueNode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String normalizeAuthorizationHeader(String value) {
|
||||
if (StringUtil.isBlank(value)) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
if (StringUtil.startsWithIgnoreCase(value, "Basic ") || StringUtil.startsWithIgnoreCase(value, "Bearer ")) {
|
||||
return value;
|
||||
}
|
||||
return "Basic " + value;
|
||||
}
|
||||
|
||||
private void validateDeptCategory(Dept dept, Dept parent) {
|
||||
if (parent == null) {
|
||||
throw new ServiceException("请选择上级组织");
|
||||
@@ -264,8 +474,10 @@ public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements ID
|
||||
|
||||
private void validateDeptCode(Dept dept, Dept parent) {
|
||||
String deptCode = dept.getDeptCode();
|
||||
// 组织编码非必填,为空时存 null,避免唯一索引冲突
|
||||
if (StringUtil.isBlank(deptCode)) {
|
||||
throw new ServiceException("组织编码不能为空");
|
||||
dept.setDeptCode(null);
|
||||
return;
|
||||
}
|
||||
deptCode = deptCode.trim();
|
||||
if (deptCode.length() > 30) {
|
||||
|
||||
+4
-1
@@ -136,7 +136,10 @@ public class DictBizServiceImpl extends ServiceImpl<DictBizMapper, DictBiz> impl
|
||||
|
||||
@Override
|
||||
public IPage<DictBizVO> parentList(Map<String, Object> dict, Query query) {
|
||||
IPage<DictBiz> page = this.page(Condition.getPage(query), Condition.getQueryWrapper(dict, DictBiz.class).lambda().eq(DictBiz::getParentId, CommonConstant.TOP_PARENT_ID).orderByAsc(DictBiz::getSort));
|
||||
IPage<DictBiz> page = this.page(Condition.getPage(query), Condition.getQueryWrapper(dict, DictBiz.class).lambda()
|
||||
.eq(DictBiz::getParentId, CommonConstant.TOP_PARENT_ID)
|
||||
.orderByAsc(DictBiz::getSort)
|
||||
.orderByDesc(DictBiz::getId));
|
||||
return DictBizWrapper.build().pageVO(page);
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -123,7 +123,10 @@ public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements ID
|
||||
|
||||
@Override
|
||||
public IPage<DictVO> parentList(Map<String, Object> dict, Query query) {
|
||||
IPage<Dict> page = this.page(Condition.getPage(query), Condition.getQueryWrapper(dict, Dict.class).lambda().eq(Dict::getParentId, CommonConstant.TOP_PARENT_ID).orderByAsc(Dict::getSort));
|
||||
IPage<Dict> page = this.page(Condition.getPage(query), Condition.getQueryWrapper(dict, Dict.class).lambda()
|
||||
.eq(Dict::getParentId, CommonConstant.TOP_PARENT_ID)
|
||||
.orderByAsc(Dict::getSort)
|
||||
.orderByDesc(Dict::getId));
|
||||
return DictWrapper.build().pageVO(page);
|
||||
}
|
||||
|
||||
|
||||
+46
-5
@@ -34,7 +34,10 @@ import org.springblade.core.mp.base.BaseServiceImpl;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.cache.DictBizCache;
|
||||
import org.springblade.system.cache.SysCache;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.system.excel.FeeItemExcel;
|
||||
import org.springblade.system.excel.FeeItemExportExcel;
|
||||
import org.springblade.system.excel.FeeItemImportFailureExcel;
|
||||
import org.springblade.system.mapper.FeeItemMapper;
|
||||
import org.springblade.system.pojo.entity.DictBiz;
|
||||
@@ -44,6 +47,7 @@ import org.springblade.system.service.IFeeItemService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@@ -62,6 +66,9 @@ public class FeeItemServiceImpl extends BaseServiceImpl<FeeItemMapper, FeeItem>
|
||||
private static final int FEE_CATEGORY_MAX_LENGTH = 50;
|
||||
private static final int NAME_MAX_LENGTH = 50;
|
||||
private static final int ENGLISH_NAME_MAX_LENGTH = 100;
|
||||
private static final int REMARK_MAX_LENGTH = 200;
|
||||
private static final BigDecimal TAX_RATE_MIN = BigDecimal.ZERO;
|
||||
private static final BigDecimal TAX_RATE_MAX = new BigDecimal("100");
|
||||
|
||||
@Override
|
||||
public IPage<FeeItemVO> selectFeeItemPage(IPage<FeeItemVO> page, FeeItemVO feeItem) {
|
||||
@@ -118,7 +125,7 @@ public class FeeItemServiceImpl extends BaseServiceImpl<FeeItemMapper, FeeItem>
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FeeItemExcel> exportFeeItem(Wrapper<FeeItem> queryWrapper) {
|
||||
public List<FeeItemExportExcel> exportFeeItem(Wrapper<FeeItem> queryWrapper) {
|
||||
return list(queryWrapper).stream().map(this::toExcel).toList();
|
||||
}
|
||||
|
||||
@@ -126,6 +133,7 @@ public class FeeItemServiceImpl extends BaseServiceImpl<FeeItemMapper, FeeItem>
|
||||
feeItem.setFeeCategory(trimToEmpty(feeItem.getFeeCategory()));
|
||||
feeItem.setName(trimToEmpty(feeItem.getName()));
|
||||
feeItem.setEnglishName(trimToNull(feeItem.getEnglishName()));
|
||||
feeItem.setRemark(trimToNull(feeItem.getRemark()));
|
||||
appendFeeCategoryPrefix(feeItem);
|
||||
if (Func.isEmpty(feeItem.getStatus())) {
|
||||
feeItem.setStatus(STATUS_ENABLED);
|
||||
@@ -151,10 +159,23 @@ public class FeeItemServiceImpl extends BaseServiceImpl<FeeItemMapper, FeeItem>
|
||||
if (Func.isNotEmpty(feeItem.getEnglishName()) && feeItem.getEnglishName().length() > ENGLISH_NAME_MAX_LENGTH) {
|
||||
throw new ServiceException("费用项代码不能超过100字");
|
||||
}
|
||||
validateUniqueName(feeItem);
|
||||
BigDecimal taxRate = feeItem.getTaxRate();
|
||||
if (taxRate == null) {
|
||||
throw new ServiceException("税率不能为空");
|
||||
}
|
||||
if (taxRate.compareTo(TAX_RATE_MIN) < 0 || taxRate.compareTo(TAX_RATE_MAX) > 0) {
|
||||
throw new ServiceException("税率必须在0到100之间");
|
||||
}
|
||||
if (taxRate.stripTrailingZeros().scale() > 2) {
|
||||
throw new ServiceException("税率最多保留2位小数");
|
||||
}
|
||||
if (Func.isNotEmpty(feeItem.getRemark()) && feeItem.getRemark().length() > REMARK_MAX_LENGTH) {
|
||||
throw new ServiceException("备注不能超过200个字");
|
||||
}
|
||||
validateUnique(feeItem);
|
||||
}
|
||||
|
||||
private void validateUniqueName(FeeItem feeItem) {
|
||||
private void validateUnique(FeeItem feeItem) {
|
||||
LambdaQueryWrapper<FeeItem> queryWrapper = Wrappers.<FeeItem>lambdaQuery()
|
||||
.eq(FeeItem::getName, feeItem.getName())
|
||||
.eq(FeeItem::getIsDeleted, 0);
|
||||
@@ -164,6 +185,19 @@ public class FeeItemServiceImpl extends BaseServiceImpl<FeeItemMapper, FeeItem>
|
||||
if (count(queryWrapper) > 0L) {
|
||||
throw new ServiceException("该费用项已存在");
|
||||
}
|
||||
|
||||
if (Func.isEmpty(feeItem.getEnglishName())) {
|
||||
return;
|
||||
}
|
||||
LambdaQueryWrapper<FeeItem> codeQueryWrapper = Wrappers.<FeeItem>lambdaQuery()
|
||||
.eq(FeeItem::getEnglishName, feeItem.getEnglishName())
|
||||
.eq(FeeItem::getIsDeleted, 0);
|
||||
if (Func.isNotEmpty(feeItem.getId())) {
|
||||
codeQueryWrapper.ne(FeeItem::getId, feeItem.getId());
|
||||
}
|
||||
if (count(codeQueryWrapper) > 0L) {
|
||||
throw new ServiceException("该费用项代码已存在");
|
||||
}
|
||||
}
|
||||
|
||||
private FeeItem buildImportFeeItem(FeeItemExcel excel) {
|
||||
@@ -171,13 +205,19 @@ public class FeeItemServiceImpl extends BaseServiceImpl<FeeItemMapper, FeeItem>
|
||||
feeItem.setFeeCategory(resolveFeeCategory(excel.getFeeCategory()));
|
||||
feeItem.setEnglishName(trimToNull(excel.getEnglishName()));
|
||||
feeItem.setName(trimToEmpty(excel.getName()));
|
||||
feeItem.setTaxRate(excel.getTaxRate());
|
||||
feeItem.setStatus(STATUS_ENABLED);
|
||||
return feeItem;
|
||||
}
|
||||
|
||||
private FeeItemExcel toExcel(FeeItem feeItem) {
|
||||
FeeItemExcel excel = Objects.requireNonNull(BeanUtil.copyProperties(feeItem, FeeItemExcel.class));
|
||||
private FeeItemExportExcel toExcel(FeeItem feeItem) {
|
||||
FeeItemExportExcel excel = Objects.requireNonNull(BeanUtil.copyProperties(feeItem, FeeItemExportExcel.class));
|
||||
excel.setFeeCategory(formatFeeCategory(feeItem.getFeeCategory()));
|
||||
excel.setCreateDeptName(
|
||||
Func.isEmpty(feeItem.getCreateDept()) ? "" : SysCache.getDeptName(feeItem.getCreateDept())
|
||||
);
|
||||
excel.setUpdateUserName(UserCache.getUserRealName(feeItem.getUpdateUser()));
|
||||
excel.setStatusName(Objects.equals(feeItem.getStatus(), STATUS_ENABLED) ? "启用" : "停用");
|
||||
return excel;
|
||||
}
|
||||
|
||||
@@ -186,6 +226,7 @@ public class FeeItemServiceImpl extends BaseServiceImpl<FeeItemMapper, FeeItem>
|
||||
failureExcel.setFeeCategory(excel.getFeeCategory());
|
||||
failureExcel.setEnglishName(excel.getEnglishName());
|
||||
failureExcel.setName(excel.getName());
|
||||
failureExcel.setTaxRate(excel.getTaxRate());
|
||||
failureExcel.setFailureReason(failureReason);
|
||||
return failureExcel;
|
||||
}
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments from this software for such purposes. Copyright of this software remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.mapper.InvoiceItemMapper;
|
||||
import org.springblade.system.pojo.entity.InvoiceItem;
|
||||
import org.springblade.system.pojo.vo.InvoiceItemVO;
|
||||
import org.springblade.system.service.IInvoiceItemService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 开票项目服务实现类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Service
|
||||
public class InvoiceItemServiceImpl extends BaseServiceImpl<InvoiceItemMapper, InvoiceItem>
|
||||
implements IInvoiceItemService {
|
||||
|
||||
private static final int SHORT_NAME_MAX_LENGTH = 100;
|
||||
private static final int TAX_CODE_MAX_LENGTH = 30;
|
||||
private static final int CATEGORY_NAME_MAX_LENGTH = 200;
|
||||
|
||||
@Override
|
||||
public IPage<InvoiceItemVO> selectInvoiceItemPage(IPage<InvoiceItemVO> page, InvoiceItemVO invoiceItem) {
|
||||
return page.setRecords(baseMapper.selectInvoiceItemPage(page, invoiceItem));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean submit(InvoiceItem invoiceItem) {
|
||||
prepare(invoiceItem);
|
||||
validate(invoiceItem);
|
||||
return saveOrUpdate(invoiceItem);
|
||||
}
|
||||
|
||||
private void prepare(InvoiceItem invoiceItem) {
|
||||
invoiceItem.setShortName(trim(invoiceItem.getShortName()));
|
||||
invoiceItem.setTaxClassificationCode(trim(invoiceItem.getTaxClassificationCode()));
|
||||
invoiceItem.setCategoryName(trim(invoiceItem.getCategoryName()));
|
||||
if (Func.isEmpty(invoiceItem.getStatus())) {
|
||||
invoiceItem.setStatus(1);
|
||||
}
|
||||
}
|
||||
|
||||
private void validate(InvoiceItem invoiceItem) {
|
||||
if (Func.isEmpty(invoiceItem.getShortName())) throw new ServiceException("货物或服务简称不能为空");
|
||||
if (invoiceItem.getShortName().length() > SHORT_NAME_MAX_LENGTH) throw new ServiceException("货物或服务简称不能超过100字");
|
||||
if (Func.isEmpty(invoiceItem.getTaxClassificationCode())) throw new ServiceException("税收分类编码不能为空");
|
||||
if (invoiceItem.getTaxClassificationCode().length() > TAX_CODE_MAX_LENGTH) throw new ServiceException("税收分类编码不能超过30字");
|
||||
if (Func.isEmpty(invoiceItem.getCategoryName())) throw new ServiceException("商品和服务分类名称不能为空");
|
||||
if (invoiceItem.getCategoryName().length() > CATEGORY_NAME_MAX_LENGTH) throw new ServiceException("商品和服务分类名称不能超过200字");
|
||||
BigDecimal taxRate = invoiceItem.getDefaultTaxRate();
|
||||
if (taxRate == null) throw new ServiceException("默认税率不能为空");
|
||||
if (taxRate.compareTo(BigDecimal.ZERO) < 0 || taxRate.compareTo(new BigDecimal("100")) > 0) throw new ServiceException("默认税率必须在0到100之间");
|
||||
boolean duplicate = count(Wrappers.<InvoiceItem>lambdaQuery()
|
||||
.eq(InvoiceItem::getTaxClassificationCode, invoiceItem.getTaxClassificationCode())
|
||||
.eq(InvoiceItem::getShortName, invoiceItem.getShortName())
|
||||
.eq(InvoiceItem::getIsDeleted, 0)
|
||||
.ne(Func.isNotEmpty(invoiceItem.getId()), InvoiceItem::getId, invoiceItem.getId())) > 0;
|
||||
if (duplicate) throw new ServiceException("该开票项目已存在");
|
||||
}
|
||||
|
||||
private String trim(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.mapper.MeasurementUnitMapper;
|
||||
import org.springblade.system.pojo.entity.MeasurementUnit;
|
||||
import org.springblade.system.pojo.vo.MeasurementUnitVO;
|
||||
import org.springblade.system.service.IMeasurementUnitService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 计量单位服务实现类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Service
|
||||
public class MeasurementUnitServiceImpl extends BaseServiceImpl<MeasurementUnitMapper, MeasurementUnit>
|
||||
implements IMeasurementUnitService {
|
||||
|
||||
private static final int STATUS_ENABLED = 1;
|
||||
private static final int STATUS_DISABLED = 2;
|
||||
private static final int UNIT_CODE_MAX_LENGTH = 50;
|
||||
private static final int UNIT_NAME_MAX_LENGTH = 50;
|
||||
private static final int DIMENSION_MAX_LENGTH = 20;
|
||||
private static final int REMARK_MAX_LENGTH = 200;
|
||||
private static final Set<String> DIMENSIONS = Set.of("重量", "体积", "数量");
|
||||
|
||||
@Override
|
||||
public IPage<MeasurementUnitVO> selectMeasurementUnitPage(IPage<MeasurementUnitVO> page,
|
||||
MeasurementUnitVO measurementUnit) {
|
||||
return page.setRecords(baseMapper.selectMeasurementUnitPage(page, measurementUnit));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean submit(MeasurementUnit measurementUnit) {
|
||||
prepare(measurementUnit);
|
||||
validate(measurementUnit);
|
||||
return saveOrUpdate(measurementUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean changeStatus(Long id, Integer status) {
|
||||
if (Func.isEmpty(id)) {
|
||||
throw new ServiceException("主键不能为空");
|
||||
}
|
||||
MeasurementUnit measurementUnit = getById(id);
|
||||
if (Func.isEmpty(measurementUnit)) {
|
||||
throw new ServiceException("计量单位不存在");
|
||||
}
|
||||
if (!Objects.equals(status, STATUS_ENABLED) && !Objects.equals(status, STATUS_DISABLED)) {
|
||||
throw new ServiceException("启停状态不正确");
|
||||
}
|
||||
MeasurementUnit update = new MeasurementUnit();
|
||||
update.setId(id);
|
||||
update.setStatus(status);
|
||||
return updateById(update);
|
||||
}
|
||||
|
||||
private void prepare(MeasurementUnit measurementUnit) {
|
||||
measurementUnit.setUnitCode(trimToEmpty(measurementUnit.getUnitCode()));
|
||||
measurementUnit.setUnitName(trimToEmpty(measurementUnit.getUnitName()));
|
||||
measurementUnit.setDimension(trimToEmpty(measurementUnit.getDimension()));
|
||||
measurementUnit.setRemark(trimToNull(measurementUnit.getRemark()));
|
||||
if (Func.isEmpty(measurementUnit.getStatus())) {
|
||||
measurementUnit.setStatus(STATUS_ENABLED);
|
||||
}
|
||||
}
|
||||
|
||||
private void validate(MeasurementUnit measurementUnit) {
|
||||
if (Func.isEmpty(measurementUnit.getUnitCode())) {
|
||||
throw new ServiceException("计量单位编码不能为空");
|
||||
}
|
||||
if (measurementUnit.getUnitCode().length() > UNIT_CODE_MAX_LENGTH) {
|
||||
throw new ServiceException("计量单位编码不能超过50字");
|
||||
}
|
||||
if (Func.isEmpty(measurementUnit.getUnitName())) {
|
||||
throw new ServiceException("计量单位不能为空");
|
||||
}
|
||||
if (measurementUnit.getUnitName().length() > UNIT_NAME_MAX_LENGTH) {
|
||||
throw new ServiceException("计量单位不能超过50字");
|
||||
}
|
||||
if (Func.isEmpty(measurementUnit.getDimension())) {
|
||||
throw new ServiceException("计量维度不能为空");
|
||||
}
|
||||
if (measurementUnit.getDimension().length() > DIMENSION_MAX_LENGTH
|
||||
|| !DIMENSIONS.contains(measurementUnit.getDimension())) {
|
||||
throw new ServiceException("计量维度不正确");
|
||||
}
|
||||
if (Func.isNotEmpty(measurementUnit.getRemark())
|
||||
&& measurementUnit.getRemark().length() > REMARK_MAX_LENGTH) {
|
||||
throw new ServiceException("备注不能超过200个字");
|
||||
}
|
||||
if (!Objects.equals(measurementUnit.getStatus(), STATUS_ENABLED)
|
||||
&& !Objects.equals(measurementUnit.getStatus(), STATUS_DISABLED)) {
|
||||
throw new ServiceException("启停状态不正确");
|
||||
}
|
||||
validateUniqueUnitCode(measurementUnit);
|
||||
validateUniqueUnitName(measurementUnit);
|
||||
}
|
||||
|
||||
private void validateUniqueUnitCode(MeasurementUnit measurementUnit) {
|
||||
LambdaQueryWrapper<MeasurementUnit> queryWrapper = Wrappers.<MeasurementUnit>lambdaQuery()
|
||||
.eq(MeasurementUnit::getUnitCode, measurementUnit.getUnitCode())
|
||||
.eq(MeasurementUnit::getIsDeleted, 0);
|
||||
if (Func.isNotEmpty(measurementUnit.getId())) {
|
||||
queryWrapper.ne(MeasurementUnit::getId, measurementUnit.getId());
|
||||
}
|
||||
if (count(queryWrapper) > 0L) {
|
||||
throw new ServiceException("该计量单位编码已存在");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateUniqueUnitName(MeasurementUnit measurementUnit) {
|
||||
LambdaQueryWrapper<MeasurementUnit> queryWrapper = Wrappers.<MeasurementUnit>lambdaQuery()
|
||||
.eq(MeasurementUnit::getUnitName, measurementUnit.getUnitName())
|
||||
.eq(MeasurementUnit::getIsDeleted, 0);
|
||||
if (Func.isNotEmpty(measurementUnit.getId())) {
|
||||
queryWrapper.ne(MeasurementUnit::getId, measurementUnit.getId());
|
||||
}
|
||||
if (count(queryWrapper) > 0L) {
|
||||
throw new ServiceException("该计量单位已存在");
|
||||
}
|
||||
}
|
||||
|
||||
private String trimToEmpty(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
String trimValue = trimToEmpty(value);
|
||||
return trimValue.isEmpty() ? null : trimValue;
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -164,6 +164,35 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IM
|
||||
return menuWrapper.listNodeVO(buttons);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> permissionCodes(String roleId) {
|
||||
List<String> permissionCodes = new ArrayList<>();
|
||||
// Feign 调用时无登录态,不能走 AuthUtil.isAdministrator() 分支;按 roleId 取按钮权限
|
||||
List<Menu> buttons = StringUtil.isBlank(roleId)
|
||||
? Collections.emptyList()
|
||||
: baseMapper.buttons(Func.toLongList(roleId));
|
||||
MenuWrapper menuWrapper = new MenuWrapper();
|
||||
collectLeafPermissionCodes(menuWrapper.listNodeVO(buttons), permissionCodes);
|
||||
return permissionCodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归收集按钮树叶子节点的权限编号(与前端 SET_PERMISSION 逻辑一致)
|
||||
*/
|
||||
private void collectLeafPermissionCodes(List<MenuVO> menuList, List<String> permissionCodes) {
|
||||
if (menuList == null || menuList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (MenuVO menu : menuList) {
|
||||
List<MenuVO> children = menu.getChildren();
|
||||
if (children != null && !children.isEmpty()) {
|
||||
collectLeafPermissionCodes(children, permissionCodes);
|
||||
} else if (StringUtil.isNotBlank(menu.getCode())) {
|
||||
permissionCodes.add(menu.getCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TreeNode> tree() {
|
||||
return ForestNodeMerger.merge(baseMapper.tree());
|
||||
|
||||
+321
-209
@@ -11,16 +11,15 @@ import org.springblade.common.constant.DictTypeEnum;
|
||||
import org.springblade.core.cache.utils.CacheUtil;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.core.tool.utils.DigestUtil;
|
||||
import org.springblade.system.cache.DictCache;
|
||||
import org.springblade.system.cache.ParamCache;
|
||||
import org.springblade.system.convert.DeptConvert;
|
||||
import org.springblade.system.convert.UserConvert;
|
||||
import org.springblade.system.log.ComposeLogUtil;
|
||||
import org.springblade.system.pojo.entity.*;
|
||||
import org.springblade.system.pojo.enums.DataSync;
|
||||
import org.springblade.system.pojo.enums.DeptCategory;
|
||||
import org.springblade.system.pojo.vo.UserDeptIdsVO;
|
||||
import org.springblade.system.pojo.vo.OaOrgSyncPageVO;
|
||||
import org.springblade.system.pojo.vo.OaPersonSyncPageVO;
|
||||
import org.springblade.system.service.*;
|
||||
import org.springblade.system.util.DataSyncRecordUtils;
|
||||
import org.springblade.thirdparty.oa.constant.OAConstant;
|
||||
@@ -29,6 +28,8 @@ import org.springblade.thirdparty.oa.feign.IOAClient;
|
||||
import org.springblade.thirdparty.oa.pojo.response.OACompanyResponse;
|
||||
import org.springblade.thirdparty.oa.pojo.response.OADepartmentResponse;
|
||||
import org.springblade.thirdparty.oa.pojo.response.OAPersonResponse;
|
||||
import org.springblade.thirdparty.oa.pojo.response.OAResponse;
|
||||
import org.springblade.thirdparty.oa.pojo.response.OAResponseData;
|
||||
import org.springblade.thirdparty.oa.pojo.search.OACompanySearch;
|
||||
import org.springblade.thirdparty.oa.pojo.search.OADepartmentSearch;
|
||||
import org.springblade.thirdparty.oa.pojo.search.OAPersonSearch;
|
||||
@@ -38,6 +39,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -54,20 +56,14 @@ import static org.springblade.core.cache.constant.CacheConstant.USER_CACHE;
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class OASyncServiceImpl implements IOASyncService {
|
||||
/**
|
||||
* 用户表部门id最大长度
|
||||
*/
|
||||
private static final int MAX_DEPT_ID_LENGTH = 2000;
|
||||
|
||||
private final IOAClient oaClient;
|
||||
private final DeptConvert deptConvert;
|
||||
private final IDeptService deptService;
|
||||
private final IUserService userService;
|
||||
private final UserConvert userConvert;
|
||||
private final IUserDeptService userDeptService;
|
||||
private final IRoleService roleService;
|
||||
private final IMKPushService mkPushService;
|
||||
private final IDataSyncRecordService dataSyncRecordService;
|
||||
private final OaUserListSyncHelper oaUserListSyncHelper;
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
@@ -104,6 +100,53 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public int syncPersonFromUserList() {
|
||||
AtomicInteger syncedCount = new AtomicInteger();
|
||||
try {
|
||||
ComposeLogUtil.addLog(log);
|
||||
this.syncAndRecord(DataSyncRecordUtils::createOAPersonFetch, startTime ->
|
||||
syncedCount.set(this.syncPersonFromOa(null)), true);
|
||||
return syncedCount.get();
|
||||
} finally {
|
||||
ComposeLogUtil.removeLastLog();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public OaPersonSyncPageVO syncPersonFromUserList(int current, int size) {
|
||||
try {
|
||||
ComposeLogUtil.addLog(log);
|
||||
return this.syncPersonFromOaPage(current, size);
|
||||
} finally {
|
||||
ComposeLogUtil.removeLastLog();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public OaOrgSyncPageVO syncCompanyPage(int current, int size) {
|
||||
try {
|
||||
ComposeLogUtil.addLog(log);
|
||||
return this.syncCompanyFromOaPage(current, size);
|
||||
} finally {
|
||||
ComposeLogUtil.removeLastLog();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public OaOrgSyncPageVO syncDepartmentPage(int current, int size) {
|
||||
try {
|
||||
ComposeLogUtil.addLog(log);
|
||||
return this.syncDepartmentFromOaPage(current, size);
|
||||
} finally {
|
||||
ComposeLogUtil.removeLastLog();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步并记录
|
||||
*
|
||||
@@ -146,20 +189,15 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
// 未处理的数据
|
||||
List<Dept> notHandleList = new ArrayList<>();
|
||||
// 1. 设置查询参数
|
||||
OACompanySearch companySearch = new OACompanySearch();
|
||||
companySearch.setCurPage(1);
|
||||
if (startTime != null) {
|
||||
// 开始时间不为空,设置修改时间参数
|
||||
companySearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME));
|
||||
}
|
||||
OACompanySearch companySearch = buildCompanySearch(startTime);
|
||||
// 2. 分页查询并处理数据
|
||||
OAUtils.pageSyncHandler(companySearch, param -> oaClient.queryCompanyPage(new OASearch<>(param)), response -> {
|
||||
ComposeLogUtil.getLastLog().error("调用OA接口查询公司信息失败 {}", JSON.toJSONString(response));
|
||||
return new ServiceException("调用OA接口查询公司信息失败");
|
||||
}, 10000, ComposeLogUtil.getLastLog()::info).accept(list -> {
|
||||
// 处理数据
|
||||
List<Dept> deptList = handleCompany(list);
|
||||
notHandleList.addAll(deptList);
|
||||
OrgSyncCount syncCount = handleCompany(list);
|
||||
notHandleList.addAll(syncCount.getNotHandledList());
|
||||
});
|
||||
// 3. 未处理的数据
|
||||
if (CollectionUtil.isNotEmpty(notHandleList)) {
|
||||
@@ -183,21 +221,15 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
// 未处理的数据
|
||||
List<Dept> notHandleList = new ArrayList<>();
|
||||
// 1. 设置查询参数
|
||||
OADepartmentSearch departmentSearch = new OADepartmentSearch();
|
||||
departmentSearch.setCurPage(1);
|
||||
departmentSearch.setSubcompanyid1(subCompanyIds);
|
||||
if (startTime != null) {
|
||||
// 开始时间不为空,设置修改时间参数
|
||||
departmentSearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME));
|
||||
}
|
||||
OADepartmentSearch departmentSearch = buildDepartmentSearch(startTime, subCompanyIds);
|
||||
// 2. 分页查询并处理数据
|
||||
OAUtils.pageSyncHandler(departmentSearch, param -> oaClient.queryDepartmentPage(new OASearch<>(param)), response -> {
|
||||
ComposeLogUtil.getLastLog().error("调用OA接口查询部门信息失败 {}", JSON.toJSONString(response));
|
||||
return new ServiceException("调用OA接口查询部门信息失败");
|
||||
}, 10000, ComposeLogUtil.getLastLog()::info).accept(list -> {
|
||||
// 处理数据
|
||||
List<Dept> deptList = handleDept(list);
|
||||
notHandleList.addAll(deptList);
|
||||
OrgSyncCount syncCount = handleDept(list);
|
||||
notHandleList.addAll(syncCount.getNotHandledList());
|
||||
});
|
||||
// 3. 未处理的数据
|
||||
if (CollectionUtil.isNotEmpty(notHandleList)) {
|
||||
@@ -213,185 +245,236 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
* @param startTime 查询开始时间
|
||||
*/
|
||||
private void syncPerson(Date startTime) {
|
||||
String subCompanyIds = getSubCompanyIds();
|
||||
if (StringUtils.isEmpty(subCompanyIds)) {
|
||||
ComposeLogUtil.getLastLog().warn("未查询到子公司查询参数");
|
||||
return;
|
||||
}
|
||||
// 1. 设置查询参数
|
||||
OAPersonSearch personSearch = new OAPersonSearch();
|
||||
personSearch.setCurPage(1);
|
||||
personSearch.setSubcompanyid1(subCompanyIds);
|
||||
if (startTime != null) {
|
||||
// 开始时间不为空,设置修改时间参数
|
||||
personSearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME));
|
||||
}
|
||||
// 2. 分页查询并处理数据
|
||||
OAUtils.pageSyncHandler(personSearch, param -> oaClient.queryPersonPage(new OASearch<>(param)), response -> {
|
||||
ComposeLogUtil.getLastLog().error("调用OA接口查询人员信息失败 {}", JSON.toJSONString(response));
|
||||
return new ServiceException("调用OA接口查询人员信息失败");
|
||||
}, 10000, ComposeLogUtil.getLastLog()::info).accept(this::handlePerson);
|
||||
// 清除用户缓存
|
||||
CacheUtil.clear(USER_CACHE);
|
||||
this.syncPersonFromOa(startTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理oa人员
|
||||
* @param oaPersons
|
||||
* 从 OA 人员列表同步组织与人员
|
||||
*
|
||||
* @param startTime 增量查询开始时间,为空则全量
|
||||
* @return 处理的人员数量
|
||||
*/
|
||||
private void handlePerson(List<OAPersonResponse> oaPersons) {
|
||||
if (CollectionUtil.isEmpty(oaPersons)) {
|
||||
// 数据为空,直接返回
|
||||
return;
|
||||
}
|
||||
// 根公司id
|
||||
String rootCompanyId = getRootCompanyId();
|
||||
if (rootCompanyId == null) {
|
||||
return;
|
||||
}
|
||||
// 根公司下要同步的部门id
|
||||
Set<String> rootCompanyDeptIds = getRootCompanyDeptIds(rootCompanyId);
|
||||
oaPersons = oaPersons.stream()
|
||||
// 公司不是根公司,或者部门在根公司下要同步的部门列表中
|
||||
.filter(oaPerson -> !rootCompanyId.equals(oaPerson.getSubcompanyid1()) || rootCompanyDeptIds.contains(oaPerson.getDepartmentid()))
|
||||
.toList();
|
||||
// 根据手机号转成set
|
||||
TreeSet<OAPersonResponse> oaPersonSet = CollectionUtil.toTreeSet(oaPersons, Comparator.comparing(OAPersonResponse::getMobile));
|
||||
// 默认密码
|
||||
String defaultPassword = DigestUtil.encrypt(ParamCache.getValue(DEFAULT_PARAM_PASSWORD));
|
||||
// 转换数据
|
||||
List<User> users = oaPersonSet.stream()
|
||||
// 只需要手机不为空的
|
||||
.filter(person -> StringUtils.isNotBlank(person.getMobile()))
|
||||
.map(person -> userConvert.person2user(person, defaultPassword))
|
||||
.toList();
|
||||
List<String> phones = users.stream()
|
||||
.map(User::getPhone)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.distinct()
|
||||
.toList();
|
||||
// 查询所有用户
|
||||
Map<String, Long> userMap = userService.list(Wrappers.<User>lambdaQuery()
|
||||
//.eq(User::getIsDeleted, BladeConstant.DB_NOT_DELETED)
|
||||
.in(User::getPhone, phones)
|
||||
).stream()
|
||||
// 解密手机号
|
||||
.peek(userService::decryptPhone)
|
||||
.collect(Collectors.toMap(User::getPhone, User::getId, (a, b) -> b));
|
||||
// 数据库存在的所有用户id
|
||||
Set<Long> existsUserIds = new HashSet<>(userMap.values());
|
||||
users.forEach(user -> {
|
||||
if (userMap.containsKey(user.getPhone())) {
|
||||
// 根据手机号获取对应的用户id
|
||||
user.setId(userMap.get(user.getPhone()));
|
||||
// 清空密码,不修改密码
|
||||
user.setPassword(null);
|
||||
} else {
|
||||
// 没有就生成一个id
|
||||
user.setId(DefaultIdentifierGenerator.getInstance().nextId(null));
|
||||
userService.encryptPhone(user);
|
||||
userMap.put(user.getPhone(), user.getId());
|
||||
}
|
||||
});
|
||||
private int syncPersonFromOa(Date startTime) {
|
||||
OAPersonSearch personSearch = buildPersonSearch(startTime);
|
||||
List<OAPersonResponse> oaPersons = new ArrayList<>();
|
||||
OAUtils.pageSyncHandler(personSearch, param -> oaClient.queryPersonPage(new OASearch<>(param)), response -> {
|
||||
ComposeLogUtil.getLastLog().error("调用OA接口查询人员信息失败 {}", JSON.toJSONString(response));
|
||||
return new ServiceException("调用OA接口查询人员信息失败");
|
||||
}, 10000, ComposeLogUtil.getLastLog()::info).accept(oaPersons::addAll);
|
||||
OaUserListSyncHelper.OaOrgIndex orgIndex = oaUserListSyncHelper.syncOrgsFromPersons(oaPersons);
|
||||
OaUserListSyncHelper.PersonSyncCount personSyncCount = oaUserListSyncHelper.handlePerson(oaPersons, orgIndex);
|
||||
CacheUtil.clear(USER_CACHE);
|
||||
CacheUtil.clear(SYS_CACHE);
|
||||
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
|
||||
return personSyncCount.getSyncedCount();
|
||||
}
|
||||
|
||||
// 不存在的新增
|
||||
List<User> addUsers = users.stream()
|
||||
.filter(user -> !existsUserIds.contains(user.getId()))
|
||||
.toList();
|
||||
if (CollectionUtil.isNotEmpty(addUsers)) {
|
||||
ComposeLogUtil.getLastLog().info("批量新增用户:{}", addUsers.size());
|
||||
userService.saveBatch(addUsers);
|
||||
/**
|
||||
* 按页从 OA 人员列表同步组织与人员
|
||||
*
|
||||
* @param current 当前页
|
||||
* @param size 每页条数
|
||||
* @return 本页同步结果
|
||||
*/
|
||||
private OaPersonSyncPageVO syncPersonFromOaPage(int current, int size) {
|
||||
int pageNo = current < 1 ? 1 : current;
|
||||
int pageSize = size < 1 ? 20 : Math.min(size, 200);
|
||||
OAPersonSearch personSearch = buildPersonSearch(null);
|
||||
personSearch.setCurPage(pageNo);
|
||||
personSearch.setPageSize(pageSize);
|
||||
OAResponse<OAPersonResponse> oaResponse = oaClient.queryPersonPage(new OASearch<>(personSearch));
|
||||
if (oaResponse == null || !OAConstant.OA_SUCCESS_CODE.equals(oaResponse.getCode()) || oaResponse.getData() == null) {
|
||||
ComposeLogUtil.getLastLog().error("调用OA接口查询人员信息失败 {}", JSON.toJSONString(oaResponse));
|
||||
throw new ServiceException("调用OA接口查询人员信息失败");
|
||||
}
|
||||
// 存在的修改
|
||||
List<User> updateUsers = users.stream()
|
||||
.filter(user -> existsUserIds.contains(user.getId()))
|
||||
.toList();
|
||||
if (CollectionUtil.isNotEmpty(updateUsers)) {
|
||||
ComposeLogUtil.getLastLog().info("批量修改用户:{}", updateUsers.size());
|
||||
userService.updateBatchById(updateUsers);
|
||||
}
|
||||
// 没有手机号的数据 = 手机号为空的数量
|
||||
long noPhoneNum = oaPersons.stream()
|
||||
.map(OAPersonResponse::getMobile)
|
||||
.filter(StringUtils::isBlank)
|
||||
.count();
|
||||
ComposeLogUtil.getLastLog().info("没有手机号的数据:{}", noPhoneNum);
|
||||
OAResponseData<OAPersonResponse> responseData = oaResponse.getData();
|
||||
List<OAPersonResponse> oaPersons = responseData.getDataList() == null
|
||||
? Collections.emptyList() : responseData.getDataList();
|
||||
long totalSize = responseData.getTotalSize() == null ? 0L : responseData.getTotalSize();
|
||||
OaUserListSyncHelper.OaOrgIndex orgIndex = oaUserListSyncHelper.syncOrgsFromPersons(oaPersons);
|
||||
OaUserListSyncHelper.PersonSyncCount personSyncCount = oaUserListSyncHelper.handlePerson(oaPersons, orgIndex);
|
||||
CacheUtil.clear(USER_CACHE);
|
||||
CacheUtil.clear(SYS_CACHE);
|
||||
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
|
||||
OaPersonSyncPageVO pageVO = new OaPersonSyncPageVO();
|
||||
pageVO.setCurrent(pageNo);
|
||||
pageVO.setSize(pageSize);
|
||||
pageVO.setTotal(totalSize);
|
||||
pageVO.setFetchedCount(oaPersons.size());
|
||||
pageVO.setSyncedCount(personSyncCount.getSyncedCount());
|
||||
pageVO.setSkippedCount(personSyncCount.getSkippedCount());
|
||||
boolean finished = oaPersons.isEmpty()
|
||||
|| oaPersons.size() < pageSize
|
||||
|| (long) pageNo * pageSize >= totalSize;
|
||||
pageVO.setFinished(finished);
|
||||
ComposeLogUtil.getLastLog().info("OA人员分页同步完成 {}/{},成功{},跳过{}",
|
||||
pageNo, totalSize, personSyncCount.getSyncedCount(), personSyncCount.getSkippedCount());
|
||||
return pageVO;
|
||||
}
|
||||
|
||||
Map<String, Long> userDeptMap = userDeptService.list(Wrappers.<UserDept>lambdaQuery()
|
||||
.in(UserDept::getUserId, userMap.values())
|
||||
).stream()
|
||||
.collect(Collectors.toMap(this::getUserDeptKey, UserDept::getId, (a, b) -> b));
|
||||
// 数据库存在的所有用户部门id
|
||||
Set<Long> existsUserDeptIds = new HashSet<>(userDeptMap.values());
|
||||
/**
|
||||
* 按页从 OA 公司接口同步公司
|
||||
*
|
||||
* @param current 当前页
|
||||
* @param size 每页条数
|
||||
* @return 本页同步结果
|
||||
*/
|
||||
private OaOrgSyncPageVO syncCompanyFromOaPage(int current, int size) {
|
||||
int pageNo = current < 1 ? 1 : current;
|
||||
int pageSize = size < 1 ? 20 : Math.min(size, 200);
|
||||
OACompanySearch companySearch = buildCompanySearch(null);
|
||||
companySearch.setCurPage(pageNo);
|
||||
companySearch.setPageSize(pageSize);
|
||||
OAResponse<OACompanyResponse> oaResponse = oaClient.queryCompanyPage(new OASearch<>(companySearch));
|
||||
if (oaResponse == null || !OAConstant.OA_SUCCESS_CODE.equals(oaResponse.getCode()) || oaResponse.getData() == null) {
|
||||
ComposeLogUtil.getLastLog().error("调用OA接口查询公司信息失败 {}", JSON.toJSONString(oaResponse));
|
||||
throw new ServiceException("调用OA接口查询公司信息失败");
|
||||
}
|
||||
OAResponseData<OACompanyResponse> responseData = oaResponse.getData();
|
||||
List<OACompanyResponse> oaCompanies = responseData.getDataList() == null
|
||||
? Collections.emptyList() : responseData.getDataList();
|
||||
long totalSize = responseData.getTotalSize() == null ? 0L : responseData.getTotalSize();
|
||||
OrgSyncCount syncCount = handleCompany(oaCompanies);
|
||||
if (CollectionUtil.isNotEmpty(syncCount.getNotHandledList())) {
|
||||
ComposeLogUtil.getLastLog().warn("同步公司,本页未处理数据:{}", JSON.toJSONString(syncCount.getNotHandledList()));
|
||||
}
|
||||
CacheUtil.clear(SYS_CACHE);
|
||||
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
|
||||
OaOrgSyncPageVO pageVO = buildOrgSyncPageVO("company", pageNo, pageSize, totalSize, oaCompanies.size(), syncCount);
|
||||
ComposeLogUtil.getLastLog().info("OA公司分页同步完成 {}/{},成功{},跳过{}",
|
||||
pageNo, totalSize, syncCount.getSyncedCount(), syncCount.getSkippedCount());
|
||||
return pageVO;
|
||||
}
|
||||
|
||||
List<UserDept> userDeptList = oaPersons.stream()
|
||||
// 只要包含用户手机号的
|
||||
.filter(oaPerson -> userMap.containsKey(oaPerson.getMobile()))
|
||||
.map(oaPerson -> userConvert.person2userDept(oaPerson, userMap))
|
||||
.toList();
|
||||
userDeptList.forEach(userDept -> {
|
||||
String userDeptKey = getUserDeptKey(userDept);
|
||||
if (userDeptMap.containsKey(userDeptKey)) {
|
||||
// 根据key获取用户部门id
|
||||
userDept.setId(userDeptMap.get(userDeptKey));
|
||||
} else {
|
||||
// 没有就生成一个id
|
||||
userDept.setId(DefaultIdentifierGenerator.getInstance().nextId(null));
|
||||
}
|
||||
});
|
||||
// 不存在的新增
|
||||
List<UserDept> addList = userDeptList.stream()
|
||||
.filter(userDept -> !existsUserDeptIds.contains(userDept.getId()))
|
||||
.toList();
|
||||
if (CollectionUtil.isNotEmpty(addList)) {
|
||||
ComposeLogUtil.getLastLog().info("批量新增用户部门:{}", addList.size());
|
||||
userDeptService.saveBatch(addList);
|
||||
/**
|
||||
* 按页从 OA 部门接口同步部门
|
||||
*
|
||||
* @param current 当前页
|
||||
* @param size 每页条数
|
||||
* @return 本页同步结果
|
||||
*/
|
||||
private OaOrgSyncPageVO syncDepartmentFromOaPage(int current, int size) {
|
||||
int pageNo = current < 1 ? 1 : current;
|
||||
int pageSize = size < 1 ? 20 : Math.min(size, 200);
|
||||
String subCompanyIds = getSubCompanyIds();
|
||||
if (StringUtils.isEmpty(subCompanyIds)) {
|
||||
ComposeLogUtil.getLastLog().warn("未查询到子公司查询参数,跳过部门同步");
|
||||
OaOrgSyncPageVO emptyPageVO = buildOrgSyncPageVO("department", pageNo, pageSize, 0L, 0, OrgSyncCount.empty());
|
||||
emptyPageVO.setFinished(true);
|
||||
return emptyPageVO;
|
||||
}
|
||||
// 存在的修改
|
||||
List<UserDept> updateList = userDeptList.stream()
|
||||
.filter(userDept -> existsUserDeptIds.contains(userDept.getId()))
|
||||
.toList();
|
||||
if (CollectionUtil.isNotEmpty(updateList)) {
|
||||
ComposeLogUtil.getLastLog().info("批量修改用户部门:{}", updateList.size());
|
||||
userDeptService.updateBatchById(updateList);
|
||||
OADepartmentSearch departmentSearch = buildDepartmentSearch(null, subCompanyIds);
|
||||
departmentSearch.setCurPage(pageNo);
|
||||
departmentSearch.setPageSize(pageSize);
|
||||
OAResponse<OADepartmentResponse> oaResponse = oaClient.queryDepartmentPage(new OASearch<>(departmentSearch));
|
||||
if (oaResponse == null || !OAConstant.OA_SUCCESS_CODE.equals(oaResponse.getCode()) || oaResponse.getData() == null) {
|
||||
ComposeLogUtil.getLastLog().error("调用OA接口查询部门信息失败 {}", JSON.toJSONString(oaResponse));
|
||||
throw new ServiceException("调用OA接口查询部门信息失败");
|
||||
}
|
||||
// 回写部门id到用户表
|
||||
Collection<Long> userIds = userMap.values();
|
||||
List<UserDeptIdsVO> list = userDeptService.queryUserDeptIds(userIds);
|
||||
// 查询没有角色的用户id
|
||||
Set<Long> noRoleUserIds = userService.list(Wrappers.<User>lambdaQuery()
|
||||
.in(User::getId, userIds)
|
||||
.isNull(User::getRoleId)
|
||||
).stream()
|
||||
.map(User::getId)
|
||||
.collect(Collectors.toSet());
|
||||
// 获取默认角色id
|
||||
String defaultRoleId = getDefaultRoleId();
|
||||
List<User> updateUserParams = list.stream()
|
||||
// 过滤掉空部门id及长度超长的
|
||||
.filter(userDeptIds -> StringUtils.isNotBlank(userDeptIds.getDeptIds()) && userDeptIds.getDeptIds().length() <= MAX_DEPT_ID_LENGTH)
|
||||
.map(userDeptIds -> {
|
||||
User user = new User();
|
||||
user.setId(userDeptIds.getUserId());
|
||||
user.setDeptId(userDeptIds.getDeptIds());
|
||||
user.setDeptCodes(userDeptIds.getDeptCodes());
|
||||
if (noRoleUserIds.contains(userDeptIds.getUserId())) {
|
||||
user.setRoleId(defaultRoleId);
|
||||
}
|
||||
return user;
|
||||
}).toList();
|
||||
userService.updateBatchById(updateUserParams);
|
||||
OAResponseData<OADepartmentResponse> responseData = oaResponse.getData();
|
||||
List<OADepartmentResponse> oaDepartments = responseData.getDataList() == null
|
||||
? Collections.emptyList() : responseData.getDataList();
|
||||
long totalSize = responseData.getTotalSize() == null ? 0L : responseData.getTotalSize();
|
||||
OrgSyncCount syncCount = handleDept(oaDepartments);
|
||||
if (CollectionUtil.isNotEmpty(syncCount.getNotHandledList())) {
|
||||
ComposeLogUtil.getLastLog().warn("同步部门,本页未处理数据:{}", JSON.toJSONString(syncCount.getNotHandledList()));
|
||||
}
|
||||
CacheUtil.clear(SYS_CACHE);
|
||||
CacheUtil.clear(SYS_CACHE, Boolean.FALSE);
|
||||
OaOrgSyncPageVO pageVO = buildOrgSyncPageVO("department", pageNo, pageSize, totalSize, oaDepartments.size(), syncCount);
|
||||
if (Boolean.TRUE.equals(pageVO.getFinished())) {
|
||||
// 部门同步完成后更新祖级列表
|
||||
deptService.updateAncestors(null);
|
||||
}
|
||||
ComposeLogUtil.getLastLog().info("OA部门分页同步完成 {}/{},成功{},跳过{}",
|
||||
pageNo, totalSize, syncCount.getSyncedCount(), syncCount.getSkippedCount());
|
||||
return pageVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装组织分页同步结果
|
||||
*/
|
||||
private OaOrgSyncPageVO buildOrgSyncPageVO(String stage, int pageNo, int pageSize, long totalSize,
|
||||
int fetchedCount, OrgSyncCount syncCount) {
|
||||
OaOrgSyncPageVO pageVO = new OaOrgSyncPageVO();
|
||||
pageVO.setStage(stage);
|
||||
pageVO.setCurrent(pageNo);
|
||||
pageVO.setSize(pageSize);
|
||||
pageVO.setTotal(totalSize);
|
||||
pageVO.setFetchedCount(fetchedCount);
|
||||
pageVO.setSyncedCount(syncCount.getSyncedCount());
|
||||
pageVO.setSkippedCount(syncCount.getSkippedCount());
|
||||
boolean finished = fetchedCount == 0
|
||||
|| fetchedCount < pageSize
|
||||
|| (long) pageNo * pageSize >= totalSize;
|
||||
pageVO.setFinished(finished);
|
||||
return pageVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装 OA 公司分页查询参数
|
||||
*
|
||||
* @param startTime 增量查询开始时间
|
||||
* @return 查询参数
|
||||
*/
|
||||
private OACompanySearch buildCompanySearch(Date startTime) {
|
||||
OACompanySearch companySearch = new OACompanySearch();
|
||||
companySearch.setCurPage(1);
|
||||
companySearch.setPageSize(20);
|
||||
if (startTime != null) {
|
||||
companySearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME));
|
||||
}
|
||||
return companySearch;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装 OA 部门分页查询参数
|
||||
*
|
||||
* @param startTime 增量查询开始时间
|
||||
* @param subCompanyIds 子公司 id 列表
|
||||
* @return 查询参数
|
||||
*/
|
||||
private OADepartmentSearch buildDepartmentSearch(Date startTime, String subCompanyIds) {
|
||||
OADepartmentSearch departmentSearch = new OADepartmentSearch();
|
||||
departmentSearch.setCurPage(1);
|
||||
departmentSearch.setPageSize(20);
|
||||
departmentSearch.setSubcompanyid1(subCompanyIds);
|
||||
if (startTime != null) {
|
||||
departmentSearch.setModified(DateUtil.format(startTime, DateUtil.PATTERN_DATETIME));
|
||||
}
|
||||
return departmentSearch;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装 OA 人员分页查询参数
|
||||
*
|
||||
* @param startTime 增量查询开始时间
|
||||
* @return 查询参数
|
||||
*/
|
||||
private OAPersonSearch buildPersonSearch(Date startTime) {
|
||||
OAPersonSearch personSearch = new OAPersonSearch();
|
||||
personSearch.setCurPage(1);
|
||||
personSearch.setPageSize(20);
|
||||
personSearch.setCreated("");
|
||||
personSearch.setWorkcode("");
|
||||
personSearch.setSubcompanyid1("");
|
||||
personSearch.setDepartmentid("");
|
||||
personSearch.setJobtitleid("");
|
||||
personSearch.setId("");
|
||||
personSearch.setLoginid("");
|
||||
personSearch.setIsadaccount("");
|
||||
personSearch.setModified(startTime == null ? "" : DateUtil.format(startTime, DateUtil.PATTERN_DATETIME));
|
||||
return personSearch;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理oa公司
|
||||
* @param oaCompanies
|
||||
* @return 未处理的数据
|
||||
* @return 同步统计
|
||||
*/
|
||||
private List<Dept> handleCompany(List<OACompanyResponse> oaCompanies) {
|
||||
private OrgSyncCount handleCompany(List<OACompanyResponse> oaCompanies) {
|
||||
if (CollectionUtil.isEmpty(oaCompanies)) {
|
||||
// 数据为空,直接返回
|
||||
return Collections.emptyList();
|
||||
return OrgSyncCount.empty();
|
||||
}
|
||||
// 获取需要的公司名称
|
||||
Set<String> companyNames = getCompanyNames();
|
||||
@@ -401,30 +484,36 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
.filter(company -> companyNames.contains(company.getSubcompanyname()))
|
||||
.map(deptConvert::company2dept)
|
||||
.toList();
|
||||
int filteredSkipCount = oaCompanies.size() - allParam.size();
|
||||
if (CollectionUtil.isEmpty(allParam)) {
|
||||
return new OrgSyncCount(0, filteredSkipCount, Collections.emptyList());
|
||||
}
|
||||
|
||||
// 查询数据库的部门,转换成map
|
||||
Map<String, Long> deptMap = getAllCompanyDeptMap();
|
||||
|
||||
// 处理部门
|
||||
return handleDept(allParam, deptMap, DeptCategory.COMPANY);
|
||||
List<Dept> notHandledList = handleDept(allParam, deptMap, DeptCategory.COMPANY);
|
||||
int syncedCount = allParam.size() - notHandledList.size();
|
||||
int skippedCount = filteredSkipCount + notHandledList.size();
|
||||
return new OrgSyncCount(syncedCount, skippedCount, notHandledList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理oa部门
|
||||
* @param oaDepts
|
||||
* @return 未处理的数据
|
||||
* @return 同步统计
|
||||
*/
|
||||
private List<Dept> handleDept(List<OADepartmentResponse> oaDepts) {
|
||||
private OrgSyncCount handleDept(List<OADepartmentResponse> oaDepts) {
|
||||
if (CollectionUtil.isEmpty(oaDepts)) {
|
||||
// 数据为空,直接返回
|
||||
return Collections.emptyList();
|
||||
return OrgSyncCount.empty();
|
||||
}
|
||||
// 查询所有公司的编码和id的map
|
||||
Map<String, Long> companyDeptMap = getAllCompanyDeptMap();
|
||||
// 根公司id
|
||||
String rootCompanyId = getRootCompanyId();
|
||||
if (rootCompanyId == null) {
|
||||
return Collections.emptyList();
|
||||
return new OrgSyncCount(0, oaDepts.size(), Collections.emptyList());
|
||||
}
|
||||
// 根公司下要同步的部门名称
|
||||
Set<String> rootCompanyDeptNames = getRootCompanyDeptNames();
|
||||
@@ -434,8 +523,9 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
.filter(oaDept -> !rootCompanyId.equals(oaDept.getSubcompanyid1()) || (OAConstant.ROOT_COMPANY_ID.equals(oaDept.getSupdepid()) && rootCompanyDeptNames.contains(oaDept.getDepartmentname())))
|
||||
.map(dept -> deptConvert.dept2dept(dept, companyDeptMap))
|
||||
.toList();
|
||||
int filteredSkipCount = oaDepts.size() - allParam.size();
|
||||
if (CollectionUtil.isEmpty(allParam)) {
|
||||
return Collections.emptyList();
|
||||
return new OrgSyncCount(0, filteredSkipCount, Collections.emptyList());
|
||||
}
|
||||
Set<String> deptCodes = allParam.stream()
|
||||
.map(Dept::getDeptCode)
|
||||
@@ -448,7 +538,10 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
.filter(dept -> StringUtils.isNotEmpty(dept.getDeptCode()))
|
||||
.collect(Collectors.toMap(Dept::getDeptCode, Dept::getId, (a, b) -> b));
|
||||
// 处理部门
|
||||
return this.handleDept(allParam, deptMap, DeptCategory.DEPT);
|
||||
List<Dept> notHandledList = this.handleDept(allParam, deptMap, DeptCategory.DEPT);
|
||||
int syncedCount = allParam.size() - notHandledList.size();
|
||||
int skippedCount = filteredSkipCount + notHandledList.size();
|
||||
return new OrgSyncCount(syncedCount, skippedCount, notHandledList);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -502,6 +595,37 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 组织同步统计
|
||||
*/
|
||||
private static class OrgSyncCount {
|
||||
private final int syncedCount;
|
||||
private final int skippedCount;
|
||||
private final List<Dept> notHandledList;
|
||||
|
||||
private OrgSyncCount(int syncedCount, int skippedCount, List<Dept> notHandledList) {
|
||||
this.syncedCount = syncedCount;
|
||||
this.skippedCount = skippedCount;
|
||||
this.notHandledList = notHandledList == null ? Collections.emptyList() : notHandledList;
|
||||
}
|
||||
|
||||
private static OrgSyncCount empty() {
|
||||
return new OrgSyncCount(0, 0, Collections.emptyList());
|
||||
}
|
||||
|
||||
private int getSyncedCount() {
|
||||
return syncedCount;
|
||||
}
|
||||
|
||||
private int getSkippedCount() {
|
||||
return skippedCount;
|
||||
}
|
||||
|
||||
private List<Dept> getNotHandledList() {
|
||||
return notHandledList;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取oa查询参数,子公司id参数
|
||||
* @return
|
||||
@@ -634,16 +758,4 @@ public class OASyncServiceImpl implements IOASyncService {
|
||||
// 部门编码去掉前缀,就是oa的id
|
||||
return dept.getDeptCode().replace(OAConvertConstant.COMPANY_OA_PREFIX, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户部门唯一标识,用户id+公司编码+部门编码
|
||||
* @param userDept
|
||||
* @return
|
||||
*/
|
||||
private String getUserDeptKey(UserDept userDept) {
|
||||
if (userDept == null) {
|
||||
return null;
|
||||
}
|
||||
return userDept.getUserId() + userDept.getCompanyCode() + userDept.getDeptCode();
|
||||
}
|
||||
}
|
||||
|
||||
+609
@@ -0,0 +1,609 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springblade.common.constant.DataStatusEnum;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.constant.BladeConstant;
|
||||
import org.springblade.core.tool.utils.DigestUtil;
|
||||
import org.springblade.system.cache.ParamCache;
|
||||
import org.springblade.system.convert.UserConvert;
|
||||
import org.springblade.system.log.ComposeLogUtil;
|
||||
import org.springblade.system.pojo.entity.Dept;
|
||||
import org.springblade.system.pojo.entity.Role;
|
||||
import org.springblade.system.pojo.entity.User;
|
||||
import org.springblade.system.pojo.entity.UserDept;
|
||||
import org.springblade.system.pojo.enums.DeptCategory;
|
||||
import org.springblade.system.pojo.vo.UserDeptIdsVO;
|
||||
import org.springblade.system.service.IDeptService;
|
||||
import org.springblade.system.service.IRoleService;
|
||||
import org.springblade.system.service.IUserDeptService;
|
||||
import org.springblade.system.service.IUserService;
|
||||
import org.springblade.thirdparty.oa.constant.OAConvertConstant;
|
||||
import org.springblade.thirdparty.oa.pojo.response.OAPersonResponse;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.springblade.common.constant.CommonConstant.DEFAULT_PARAM_PASSWORD;
|
||||
import static org.springblade.common.constant.CommonConstant.DEFAULT_PARAM_ROLE;
|
||||
import static org.springblade.common.constant.CommonConstant.DEFAULT_ROLE;
|
||||
import static org.springblade.common.constant.CommonConstant.YES;
|
||||
|
||||
/**
|
||||
* 从 OA 人员列表提取组织并同步人员
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class OaUserListSyncHelper {
|
||||
|
||||
private static final int MAX_DEPT_ID_LENGTH = 2000;
|
||||
|
||||
private final IDeptService deptService;
|
||||
private final IUserService userService;
|
||||
private final UserConvert userConvert;
|
||||
private final IUserDeptService userDeptService;
|
||||
private final IRoleService roleService;
|
||||
|
||||
/**
|
||||
* 从人员数据提取二级公司、三级部门,并挂到「桂物物流集团」下
|
||||
*
|
||||
* @param oaPersons OA人员
|
||||
* @return 组织索引
|
||||
*/
|
||||
public OaOrgIndex syncOrgsFromPersons(List<OAPersonResponse> oaPersons) {
|
||||
OaOrgIndex orgIndex = new OaOrgIndex();
|
||||
if (CollectionUtil.isEmpty(oaPersons)) {
|
||||
return orgIndex;
|
||||
}
|
||||
Date orgSyncStart = new Date();
|
||||
Dept rootCompany = this.getOrCreateRootCompany();
|
||||
String tenantId = resolveTenantId();
|
||||
Map<String, OAPersonResponse> companyPersonMap = new LinkedHashMap<>();
|
||||
Map<String, OAPersonResponse> departmentPersonMap = new LinkedHashMap<>();
|
||||
for (OAPersonResponse oaPerson : oaPersons) {
|
||||
String companyKey = resolveCompanyKey(oaPerson);
|
||||
if (StringUtils.isNotBlank(companyKey) && StringUtils.isNotBlank(oaPerson.getSubcompanyname())) {
|
||||
companyPersonMap.putIfAbsent(companyKey, oaPerson);
|
||||
}
|
||||
String departmentKey = resolveDepartmentKey(oaPerson);
|
||||
if (StringUtils.isNotBlank(departmentKey) && StringUtils.isNotBlank(oaPerson.getDepartmentname())
|
||||
&& StringUtils.isNotBlank(oaPerson.getSubcompanyname())) {
|
||||
departmentPersonMap.putIfAbsent(departmentKey, oaPerson);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Dept> existingCompanyByCode = new HashMap<>();
|
||||
Map<String, Dept> existingCompanyByName = new HashMap<>();
|
||||
deptService.list(Wrappers.<Dept>lambdaQuery()
|
||||
.eq(Dept::getParentId, rootCompany.getId())
|
||||
.eq(Dept::getDeptCategory, DeptCategory.COMPANY.getCode())
|
||||
).forEach(dept -> {
|
||||
if (StringUtils.isNotBlank(dept.getDeptCode())) {
|
||||
existingCompanyByCode.put(dept.getDeptCode(), dept);
|
||||
}
|
||||
if (StringUtils.isNotBlank(dept.getDeptName())) {
|
||||
existingCompanyByName.put(dept.getDeptName(), dept);
|
||||
}
|
||||
});
|
||||
List<Dept> addCompanies = new ArrayList<>();
|
||||
List<Dept> updateCompanies = new ArrayList<>();
|
||||
companyPersonMap.forEach((companyKey, oaPerson) -> {
|
||||
String oaCode = buildCompanyCode(oaPerson, companyKey);
|
||||
Dept existing = existingCompanyByCode.get(oaCode);
|
||||
if (existing == null) {
|
||||
existing = existingCompanyByName.get(oaPerson.getSubcompanyname());
|
||||
}
|
||||
Dept company = this.upsertOrg(oaPerson.getSubcompanyname(), oaCode, existing, rootCompany, tenantId,
|
||||
DeptCategory.COMPANY, addCompanies, updateCompanies);
|
||||
orgIndex.companyByOaId.put(companyKey, company);
|
||||
});
|
||||
this.saveOrgs(addCompanies, updateCompanies, DeptCategory.COMPANY);
|
||||
|
||||
Map<String, Dept> existingDeptByCode = new HashMap<>();
|
||||
Map<String, Dept> existingDeptByParentAndName = new HashMap<>();
|
||||
List<Long> companyIds = orgIndex.companyByOaId.values().stream()
|
||||
.map(Dept::getId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (CollectionUtil.isNotEmpty(companyIds)) {
|
||||
deptService.list(Wrappers.<Dept>lambdaQuery()
|
||||
.in(Dept::getParentId, companyIds)
|
||||
.eq(Dept::getDeptCategory, DeptCategory.DEPT.getCode())
|
||||
).forEach(dept -> {
|
||||
if (StringUtils.isNotBlank(dept.getDeptCode())) {
|
||||
existingDeptByCode.put(dept.getDeptCode(), dept);
|
||||
}
|
||||
if (dept.getParentId() != null && StringUtils.isNotBlank(dept.getDeptName())) {
|
||||
existingDeptByParentAndName.put(dept.getParentId() + "#" + dept.getDeptName(), dept);
|
||||
}
|
||||
});
|
||||
}
|
||||
List<Dept> addDepartments = new ArrayList<>();
|
||||
List<Dept> updateDepartments = new ArrayList<>();
|
||||
departmentPersonMap.forEach((departmentKey, oaPerson) -> {
|
||||
Dept parentCompany = orgIndex.findCompany(oaPerson);
|
||||
if (parentCompany == null || parentCompany.getId() == null) {
|
||||
return;
|
||||
}
|
||||
String oaCode = buildDepartmentCode(oaPerson, departmentKey);
|
||||
Dept existing = existingDeptByCode.get(oaCode);
|
||||
if (existing == null) {
|
||||
existing = existingDeptByParentAndName.get(parentCompany.getId() + "#" + oaPerson.getDepartmentname());
|
||||
}
|
||||
Dept department = this.upsertOrg(oaPerson.getDepartmentname(), oaCode, existing, parentCompany, tenantId,
|
||||
DeptCategory.DEPT, addDepartments, updateDepartments);
|
||||
orgIndex.deptByOaId.put(departmentKey, department);
|
||||
orgIndex.deptByCompanyAndName.put(resolveCompanyKey(oaPerson) + "#" + oaPerson.getDepartmentname(), department);
|
||||
});
|
||||
this.saveOrgs(addDepartments, updateDepartments, DeptCategory.DEPT);
|
||||
deptService.updateAncestors(orgSyncStart);
|
||||
ComposeLogUtil.getLastLog().info("同步人员提取组织完成,二级公司{}个,三级部门{}个",
|
||||
orgIndex.companyByOaId.size(), orgIndex.deptByOaId.size());
|
||||
return orgIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步人员并绑定到三级部门
|
||||
*
|
||||
* @param oaPersons OA人员
|
||||
* @param orgIndex 组织索引
|
||||
* @return 本批同步成功与跳过数量
|
||||
*/
|
||||
public PersonSyncCount handlePerson(List<OAPersonResponse> oaPersons, OaOrgIndex orgIndex) {
|
||||
if (CollectionUtil.isEmpty(oaPersons)) {
|
||||
return new PersonSyncCount(0, 0);
|
||||
}
|
||||
Map<String, OAPersonResponse> uniquePersonMap = new LinkedHashMap<>();
|
||||
int skippedCount = 0;
|
||||
for (OAPersonResponse oaPerson : oaPersons) {
|
||||
String account = userConvert.resolveAccount(oaPerson);
|
||||
if (StringUtils.isBlank(account)) {
|
||||
skippedCount++;
|
||||
continue;
|
||||
}
|
||||
uniquePersonMap.putIfAbsent(account, oaPerson);
|
||||
}
|
||||
if (uniquePersonMap.isEmpty()) {
|
||||
ComposeLogUtil.getLastLog().warn("OA人员均缺少loginid/工号/手机号,跳过人员同步");
|
||||
return new PersonSyncCount(0, skippedCount);
|
||||
}
|
||||
String tenantId = resolveTenantId();
|
||||
String defaultPassword = DigestUtil.encrypt(ParamCache.getValue(DEFAULT_PARAM_PASSWORD));
|
||||
List<User> users = uniquePersonMap.values().stream()
|
||||
.map(person -> {
|
||||
User user = userConvert.person2user(person, defaultPassword);
|
||||
user.setTenantId(tenantId);
|
||||
return user;
|
||||
})
|
||||
.toList();
|
||||
List<String> accounts = users.stream()
|
||||
.map(User::getAccount)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.distinct()
|
||||
.toList();
|
||||
List<String> phones = users.stream()
|
||||
.map(User::getPhone)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.distinct()
|
||||
.toList();
|
||||
Map<String, User> existingByAccount = new HashMap<>();
|
||||
Map<String, User> existingByPhone = new HashMap<>();
|
||||
userService.list(Wrappers.<User>lambdaQuery()
|
||||
.and(wrapper -> {
|
||||
wrapper.in(User::getAccount, accounts);
|
||||
if (CollectionUtil.isNotEmpty(phones)) {
|
||||
wrapper.or().in(User::getPhone, phones);
|
||||
}
|
||||
})
|
||||
).stream()
|
||||
.peek(userService::decryptPhone)
|
||||
.forEach(user -> {
|
||||
if (StringUtils.isNotBlank(user.getAccount())) {
|
||||
existingByAccount.put(user.getAccount(), user);
|
||||
}
|
||||
if (StringUtils.isNotBlank(user.getPhone())) {
|
||||
existingByPhone.put(user.getPhone(), user);
|
||||
}
|
||||
});
|
||||
Map<String, Long> userMap = new HashMap<>();
|
||||
Set<Long> existsUserIds = new HashSet<>();
|
||||
users.forEach(user -> {
|
||||
User existingUser = existingByAccount.get(user.getAccount());
|
||||
if (existingUser == null && StringUtils.isNotBlank(user.getPhone())) {
|
||||
existingUser = existingByPhone.get(user.getPhone());
|
||||
}
|
||||
if (existingUser != null) {
|
||||
user.setId(existingUser.getId());
|
||||
user.setPassword(null);
|
||||
existsUserIds.add(existingUser.getId());
|
||||
} else {
|
||||
user.setId(DefaultIdentifierGenerator.getInstance().nextId(null));
|
||||
user.setPostId("-1");
|
||||
user.setPersonCategory(1);
|
||||
user.setDataScopeRange(1);
|
||||
user.setDataLevelRange(1);
|
||||
user.setIncludeNewCustomer(0);
|
||||
userService.encryptPhone(user);
|
||||
}
|
||||
userMap.put(user.getAccount(), user.getId());
|
||||
});
|
||||
|
||||
List<User> addUsers = users.stream()
|
||||
.filter(user -> !existsUserIds.contains(user.getId()))
|
||||
.toList();
|
||||
if (CollectionUtil.isNotEmpty(addUsers)) {
|
||||
ComposeLogUtil.getLastLog().info("批量新增用户:{}", addUsers.size());
|
||||
userService.saveBatch(addUsers);
|
||||
}
|
||||
List<User> updateUsers = users.stream()
|
||||
.filter(user -> existsUserIds.contains(user.getId()))
|
||||
.toList();
|
||||
if (CollectionUtil.isNotEmpty(updateUsers)) {
|
||||
ComposeLogUtil.getLastLog().info("批量修改用户:{}", updateUsers.size());
|
||||
userService.updateBatchById(updateUsers);
|
||||
}
|
||||
ComposeLogUtil.getLastLog().info("缺少账号已跳过的人员:{}", skippedCount);
|
||||
if (userMap.isEmpty()) {
|
||||
return new PersonSyncCount(0, skippedCount);
|
||||
}
|
||||
|
||||
Map<String, Long> userDeptMap = userDeptService.list(Wrappers.<UserDept>lambdaQuery()
|
||||
.in(UserDept::getUserId, userMap.values())
|
||||
).stream()
|
||||
.collect(Collectors.toMap(this::getUserDeptKey, UserDept::getId, (first, second) -> second));
|
||||
Set<Long> existsUserDeptIds = new HashSet<>(userDeptMap.values());
|
||||
List<UserDept> userDeptList = oaPersons.stream()
|
||||
.filter(oaPerson -> userMap.containsKey(userConvert.resolveAccount(oaPerson)))
|
||||
.map(oaPerson -> this.buildUserDept(oaPerson, userMap, orgIndex))
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
userDeptList.forEach(userDept -> {
|
||||
String userDeptKey = getUserDeptKey(userDept);
|
||||
if (userDeptMap.containsKey(userDeptKey)) {
|
||||
userDept.setId(userDeptMap.get(userDeptKey));
|
||||
} else {
|
||||
userDept.setId(DefaultIdentifierGenerator.getInstance().nextId(null));
|
||||
}
|
||||
});
|
||||
List<UserDept> addList = userDeptList.stream()
|
||||
.filter(userDept -> !existsUserDeptIds.contains(userDept.getId()))
|
||||
.toList();
|
||||
if (CollectionUtil.isNotEmpty(addList)) {
|
||||
ComposeLogUtil.getLastLog().info("批量新增用户部门:{}", addList.size());
|
||||
userDeptService.saveBatch(addList);
|
||||
}
|
||||
List<UserDept> updateList = userDeptList.stream()
|
||||
.filter(userDept -> existsUserDeptIds.contains(userDept.getId()))
|
||||
.toList();
|
||||
if (CollectionUtil.isNotEmpty(updateList)) {
|
||||
ComposeLogUtil.getLastLog().info("批量修改用户部门:{}", updateList.size());
|
||||
userDeptService.updateBatchById(updateList);
|
||||
}
|
||||
Collection<Long> userIds = userMap.values();
|
||||
List<UserDeptIdsVO> list = userDeptService.queryUserDeptIds(userIds);
|
||||
Set<Long> noRoleUserIds = userService.list(Wrappers.<User>lambdaQuery()
|
||||
.in(User::getId, userIds)
|
||||
.and(wrapper -> wrapper.isNull(User::getRoleId)
|
||||
.or().eq(User::getRoleId, "")
|
||||
.or().eq(User::getRoleId, "-1"))
|
||||
).stream()
|
||||
.map(User::getId)
|
||||
.collect(Collectors.toSet());
|
||||
String defaultRoleId = getDefaultRoleId();
|
||||
List<User> updateUserParams = list.stream()
|
||||
.filter(userDeptIds -> StringUtils.isNotBlank(userDeptIds.getDeptIds()) && userDeptIds.getDeptIds().length() <= MAX_DEPT_ID_LENGTH)
|
||||
.map(userDeptIds -> {
|
||||
User user = new User();
|
||||
user.setId(userDeptIds.getUserId());
|
||||
user.setDeptId(userDeptIds.getDeptIds());
|
||||
user.setDeptCodes(userDeptIds.getDeptCodes());
|
||||
if (noRoleUserIds.contains(userDeptIds.getUserId())) {
|
||||
user.setRoleId(defaultRoleId);
|
||||
}
|
||||
return user;
|
||||
}).toList();
|
||||
if (CollectionUtil.isNotEmpty(updateUserParams)) {
|
||||
userService.updateBatchById(updateUserParams);
|
||||
}
|
||||
return new PersonSyncCount(users.size(), skippedCount);
|
||||
}
|
||||
|
||||
private Dept getOrCreateRootCompany() {
|
||||
Dept rootCompany = deptService.getOne(Wrappers.<Dept>lambdaQuery()
|
||||
.eq(Dept::getDeptName, OAConvertConstant.ROOT_COMPANY_NAME)
|
||||
.last("limit 1"), false);
|
||||
if (rootCompany != null) {
|
||||
return rootCompany;
|
||||
}
|
||||
rootCompany = new Dept();
|
||||
rootCompany.setId(DefaultIdentifierGenerator.getInstance().nextId(null));
|
||||
rootCompany.setTenantId(resolveTenantId());
|
||||
rootCompany.setParentId(BladeConstant.TOP_PARENT_ID);
|
||||
rootCompany.setAncestors(String.valueOf(BladeConstant.TOP_PARENT_ID));
|
||||
rootCompany.setDeptName(OAConvertConstant.ROOT_COMPANY_NAME);
|
||||
rootCompany.setFullName(OAConvertConstant.ROOT_COMPANY_NAME);
|
||||
rootCompany.setShortName(OAConvertConstant.ROOT_COMPANY_NAME);
|
||||
rootCompany.setDeptCode("OACROOT");
|
||||
rootCompany.setParentCode(String.valueOf(BladeConstant.TOP_PARENT_ID));
|
||||
rootCompany.setBelongCompanyCode("OACROOT");
|
||||
rootCompany.setDeptCategory(DeptCategory.COMPANY.getCode());
|
||||
rootCompany.setSort(0);
|
||||
rootCompany.setStatus(DataStatusEnum.ENABLE.getCode());
|
||||
rootCompany.setIsDeleted(BladeConstant.DB_NOT_DELETED);
|
||||
rootCompany.setIsOa(YES);
|
||||
rootCompany.setIsPlatformCompany(0);
|
||||
rootCompany.setSyncTime(new Date());
|
||||
deptService.save(rootCompany);
|
||||
ComposeLogUtil.getLastLog().info("已创建顶级组织:{}", OAConvertConstant.ROOT_COMPANY_NAME);
|
||||
return rootCompany;
|
||||
}
|
||||
|
||||
private Dept upsertOrg(String name, String oaCode, Dept existing, Dept parent, String tenantId,
|
||||
DeptCategory deptCategory, List<Dept> addList, List<Dept> updateList) {
|
||||
if (existing != null) {
|
||||
Dept updateParam = new Dept();
|
||||
updateParam.setId(existing.getId());
|
||||
updateParam.setDeptName(name);
|
||||
updateParam.setFullName(name);
|
||||
updateParam.setShortName(name);
|
||||
updateParam.setParentId(parent.getId());
|
||||
updateParam.setParentCode(parent.getDeptCode());
|
||||
updateParam.setAncestors(buildAncestors(parent));
|
||||
updateParam.setIsOa(YES);
|
||||
updateParam.setSyncTime(new Date());
|
||||
updateList.add(updateParam);
|
||||
existing.setDeptName(name);
|
||||
existing.setFullName(name);
|
||||
existing.setShortName(name);
|
||||
existing.setParentId(parent.getId());
|
||||
existing.setParentCode(parent.getDeptCode());
|
||||
existing.setAncestors(updateParam.getAncestors());
|
||||
return existing;
|
||||
}
|
||||
Dept dept = this.buildOrgDept(name, oaCode, parent, tenantId, deptCategory);
|
||||
dept.setId(DefaultIdentifierGenerator.getInstance().nextId(null));
|
||||
addList.add(dept);
|
||||
return dept;
|
||||
}
|
||||
|
||||
private void saveOrgs(List<Dept> addList, List<Dept> updateList, DeptCategory deptCategory) {
|
||||
if (CollectionUtil.isNotEmpty(addList)) {
|
||||
ComposeLogUtil.getLastLog().info("批量新增{}:{}", deptCategory.getName(), addList.size());
|
||||
deptService.saveBatch(addList);
|
||||
}
|
||||
if (CollectionUtil.isNotEmpty(updateList)) {
|
||||
ComposeLogUtil.getLastLog().info("批量修改{}:{}", deptCategory.getName(), updateList.size());
|
||||
deptService.updateBatchById(updateList);
|
||||
}
|
||||
}
|
||||
|
||||
private Dept buildOrgDept(String name, String deptCode, Dept parent, String tenantId, DeptCategory deptCategory) {
|
||||
Dept dept = new Dept();
|
||||
dept.setTenantId(tenantId);
|
||||
dept.setParentId(parent.getId());
|
||||
dept.setParentCode(parent.getDeptCode());
|
||||
dept.setAncestors(this.buildAncestors(parent));
|
||||
dept.setDeptName(name);
|
||||
dept.setFullName(name);
|
||||
dept.setShortName(name);
|
||||
dept.setDeptCode(deptCode);
|
||||
dept.setBelongCompanyCode(DeptCategory.COMPANY.equals(deptCategory) ? deptCode : parent.getBelongCompanyCode());
|
||||
dept.setDeptCategory(deptCategory.getCode());
|
||||
dept.setSort(0);
|
||||
dept.setStatus(DataStatusEnum.ENABLE.getCode());
|
||||
dept.setIsDeleted(BladeConstant.DB_NOT_DELETED);
|
||||
dept.setIsOa(YES);
|
||||
dept.setIsPlatformCompany(0);
|
||||
dept.setSyncTime(new Date());
|
||||
return dept;
|
||||
}
|
||||
|
||||
private UserDept buildUserDept(OAPersonResponse oaPerson, Map<String, Long> userMap, OaOrgIndex orgIndex) {
|
||||
Dept department = orgIndex.findDept(oaPerson);
|
||||
if (department == null || department.getId() == null) {
|
||||
return null;
|
||||
}
|
||||
UserDept userDept = userConvert.person2userDept(oaPerson, userMap);
|
||||
if (userDept.getUserId() == null) {
|
||||
return null;
|
||||
}
|
||||
Dept company = orgIndex.findCompany(oaPerson);
|
||||
userDept.setDeptId(department.getId());
|
||||
userDept.setDeptCode(department.getDeptCode());
|
||||
userDept.setDeptName(department.getDeptName());
|
||||
if (company != null) {
|
||||
userDept.setCompanyCode(company.getDeptCode());
|
||||
userDept.setCompanyName(company.getDeptName());
|
||||
}
|
||||
return userDept;
|
||||
}
|
||||
|
||||
private String getDefaultRoleId() {
|
||||
String defaultRole = ParamCache.getValue(DEFAULT_PARAM_ROLE);
|
||||
if (defaultRole == null) {
|
||||
defaultRole = DEFAULT_ROLE;
|
||||
}
|
||||
List<Role> roleList = roleService.list(Wrappers.<Role>lambdaQuery()
|
||||
.eq(Role::getRoleAlias, defaultRole)
|
||||
);
|
||||
if (CollectionUtil.isEmpty(roleList)) {
|
||||
return null;
|
||||
}
|
||||
return roleList.get(0).getId().toString();
|
||||
}
|
||||
|
||||
private String resolveCompanyKey(OAPersonResponse oaPerson) {
|
||||
if (oaPerson == null) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isNotBlank(oaPerson.getSubcompanyid1())) {
|
||||
return oaPerson.getSubcompanyid1().trim();
|
||||
}
|
||||
if (StringUtils.isNotBlank(oaPerson.getSubcompanyname())) {
|
||||
return "NAME:" + oaPerson.getSubcompanyname().trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String resolveDepartmentKey(OAPersonResponse oaPerson) {
|
||||
if (oaPerson == null) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isNotBlank(oaPerson.getDepartmentid())) {
|
||||
return oaPerson.getDepartmentid().trim();
|
||||
}
|
||||
String companyKey = resolveCompanyKey(oaPerson);
|
||||
if (StringUtils.isNotBlank(companyKey) && StringUtils.isNotBlank(oaPerson.getDepartmentname())) {
|
||||
return companyKey + ":" + oaPerson.getDepartmentname().trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String buildCompanyCode(OAPersonResponse oaPerson, String companyKey) {
|
||||
if (StringUtils.isNotBlank(oaPerson.getSubcompanyid1())) {
|
||||
return OAConvertConstant.COMPANY_OA_PREFIX + oaPerson.getSubcompanyid1().trim();
|
||||
}
|
||||
return OAConvertConstant.COMPANY_OA_PREFIX + "N" + Math.abs(companyKey.hashCode());
|
||||
}
|
||||
|
||||
private String buildDepartmentCode(OAPersonResponse oaPerson, String departmentKey) {
|
||||
if (StringUtils.isNotBlank(oaPerson.getDepartmentid())) {
|
||||
return OAConvertConstant.DEPARTMENT_OA_PREFIX + oaPerson.getDepartmentid().trim();
|
||||
}
|
||||
return OAConvertConstant.DEPARTMENT_OA_PREFIX + "N" + Math.abs(departmentKey.hashCode());
|
||||
}
|
||||
|
||||
private String buildAncestors(Dept parent) {
|
||||
String ancestors = parent.getAncestors();
|
||||
if (StringUtils.isBlank(ancestors)) {
|
||||
ancestors = String.valueOf(BladeConstant.TOP_PARENT_ID);
|
||||
}
|
||||
return ancestors + "," + parent.getId();
|
||||
}
|
||||
|
||||
private String resolveTenantId() {
|
||||
String tenantId = AuthUtil.getTenantId();
|
||||
if (StringUtils.isBlank(tenantId)) {
|
||||
return BladeConstant.ADMIN_TENANT_ID;
|
||||
}
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
private String getUserDeptKey(UserDept userDept) {
|
||||
if (userDept == null) {
|
||||
return null;
|
||||
}
|
||||
return userDept.getUserId() + userDept.getCompanyCode() + userDept.getDeptCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 本批人员同步计数
|
||||
*/
|
||||
public static class PersonSyncCount {
|
||||
private final int syncedCount;
|
||||
private final int skippedCount;
|
||||
|
||||
public PersonSyncCount(int syncedCount, int skippedCount) {
|
||||
this.syncedCount = syncedCount;
|
||||
this.skippedCount = skippedCount;
|
||||
}
|
||||
|
||||
public int getSyncedCount() {
|
||||
return syncedCount;
|
||||
}
|
||||
|
||||
public int getSkippedCount() {
|
||||
return skippedCount;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* OA 组织索引
|
||||
*/
|
||||
public static class OaOrgIndex {
|
||||
private final Map<String, Dept> companyByOaId = new HashMap<>();
|
||||
private final Map<String, Dept> deptByOaId = new HashMap<>();
|
||||
private final Map<String, Dept> deptByCompanyAndName = new HashMap<>();
|
||||
|
||||
private Dept findCompany(OAPersonResponse oaPerson) {
|
||||
if (oaPerson == null) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isNotBlank(oaPerson.getSubcompanyid1())) {
|
||||
Dept company = companyByOaId.get(oaPerson.getSubcompanyid1().trim());
|
||||
if (company != null) {
|
||||
return company;
|
||||
}
|
||||
}
|
||||
if (StringUtils.isNotBlank(oaPerson.getSubcompanyname())) {
|
||||
return companyByOaId.get("NAME:" + oaPerson.getSubcompanyname().trim());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Dept findDept(OAPersonResponse oaPerson) {
|
||||
if (oaPerson == null) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isNotBlank(oaPerson.getDepartmentid())) {
|
||||
Dept department = deptByOaId.get(oaPerson.getDepartmentid().trim());
|
||||
if (department != null) {
|
||||
return department;
|
||||
}
|
||||
}
|
||||
String companyKey = StringUtils.isNotBlank(oaPerson.getSubcompanyid1())
|
||||
? oaPerson.getSubcompanyid1().trim()
|
||||
: (StringUtils.isNotBlank(oaPerson.getSubcompanyname()) ? "NAME:" + oaPerson.getSubcompanyname().trim() : null);
|
||||
if (StringUtils.isNotBlank(companyKey) && StringUtils.isNotBlank(oaPerson.getDepartmentname())) {
|
||||
Dept department = deptByCompanyAndName.get(companyKey + "#" + oaPerson.getDepartmentname());
|
||||
if (department != null) {
|
||||
return department;
|
||||
}
|
||||
return deptByOaId.get(companyKey + ":" + oaPerson.getDepartmentname().trim());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+631
-611
File diff suppressed because it is too large
Load Diff
+218
-17
@@ -34,7 +34,9 @@ import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.system.excel.RailwayStationExcel;
|
||||
import org.springblade.system.excel.RailwayStationExportExcel;
|
||||
import org.springblade.system.mapper.RailwayStationMapper;
|
||||
import org.springblade.system.pojo.entity.RailwayStation;
|
||||
import org.springblade.system.pojo.entity.Region;
|
||||
@@ -43,12 +45,15 @@ import org.springblade.system.service.IRailwayStationService;
|
||||
import org.springblade.system.service.IRegionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.interceptor.TransactionAspectSupport;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -69,7 +74,7 @@ public class RailwayStationServiceImpl extends BaseServiceImpl<RailwayStationMap
|
||||
private static final String SOURCE_MANUAL = "手动";
|
||||
private static final String SOURCE_MANUAL_RECORD = "手动录入";
|
||||
private static final String SOURCE_MANUAL_OLD = "手工导入";
|
||||
private static final String DEFAULT_COUNTRY_CODE = "+86";
|
||||
private static final int PROVINCE_REGION_LEVEL = 1;
|
||||
private static final int STATUS_ENABLED = 1;
|
||||
private static final int STATUS_DISABLED = 2;
|
||||
private static final int CODE_MAX_LENGTH = 20;
|
||||
@@ -127,35 +132,223 @@ public class RailwayStationServiceImpl extends BaseServiceImpl<RailwayStationMap
|
||||
throw new ServiceException("导入数据不能为空");
|
||||
}
|
||||
List<RailwayStationExcel> errorList = new ArrayList<>();
|
||||
List<RailwayStation> railwayStationList = new ArrayList<>();
|
||||
Map<String, Integer> tmisCodeCountMap = buildImportValueCountMap(data.stream()
|
||||
.map(excel -> trimToEmpty(excel.getTmisCode()))
|
||||
.toList());
|
||||
Map<String, Integer> telegraphCodeCountMap = buildImportValueCountMap(data.stream()
|
||||
.map(excel -> trimToEmpty(excel.getTelegraphCode()).toUpperCase(Locale.ROOT))
|
||||
.toList());
|
||||
for (int index = 0; index < data.size(); index++) {
|
||||
RailwayStationExcel excel = data.get(index);
|
||||
RailwayStation railwayStation = Objects.requireNonNull(BeanUtil.copyProperties(excel, RailwayStation.class));
|
||||
List<String> validationErrors = new ArrayList<>();
|
||||
railwayStation.setLongitude(parseImportCoordinate(excel.getLongitude(), "经度", validationErrors));
|
||||
railwayStation.setLatitude(parseImportCoordinate(excel.getLatitude(), "纬度", validationErrors));
|
||||
railwayStation.setDataSource(SOURCE_BATCH);
|
||||
railwayStation.setStatus(STATUS_ENABLED);
|
||||
normalizeImportRailwayStation(railwayStation);
|
||||
validationErrors.addAll(validateImportRailwayStation(railwayStation, tmisCodeCountMap, telegraphCodeCountMap));
|
||||
if (Func.isNotEmpty(validationErrors)) {
|
||||
excel.setErrorMessage(formatImportErrorMessage(validationErrors));
|
||||
errorList.add(excel);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
RailwayStation railwayStation = Objects.requireNonNull(BeanUtil.copyProperties(excel, RailwayStation.class));
|
||||
railwayStation.setLongitude(parseCoordinate(excel.getLongitude(), "经度"));
|
||||
railwayStation.setLatitude(parseCoordinate(excel.getLatitude(), "纬度"));
|
||||
railwayStation.setDataSource(SOURCE_BATCH);
|
||||
railwayStation.setStatus(STATUS_ENABLED);
|
||||
prepare(railwayStation, SOURCE_BATCH);
|
||||
validate(railwayStation);
|
||||
save(railwayStation);
|
||||
railwayStationList.add(railwayStation);
|
||||
} catch (Exception exception) {
|
||||
String message = exception instanceof ServiceException ? exception.getMessage() : "导入失败";
|
||||
excel.setErrorMessage("第" + (index + 2) + "行:" + message);
|
||||
excel.setErrorMessage(formatImportErrorMessage(List.of(message)));
|
||||
errorList.add(excel);
|
||||
}
|
||||
}
|
||||
if (Func.isNotEmpty(errorList)) {
|
||||
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
|
||||
return errorList;
|
||||
}
|
||||
for (RailwayStation railwayStation : railwayStationList) {
|
||||
prepareImportTarget(railwayStation);
|
||||
if (!saveOrUpdate(railwayStation)) {
|
||||
throw new ServiceException("铁路车站保存失败");
|
||||
}
|
||||
}
|
||||
return errorList;
|
||||
}
|
||||
|
||||
private void prepareImportTarget(RailwayStation railwayStation) {
|
||||
if (Func.isNotEmpty(railwayStation.getId())) {
|
||||
return;
|
||||
}
|
||||
RailwayStation existingRailwayStation = baseMapper.selectByCodeIncludingDeleted(railwayStation.getCode());
|
||||
if (existingRailwayStation == null) {
|
||||
return;
|
||||
}
|
||||
if (!Objects.equals(existingRailwayStation.getIsDeleted(), 1)) {
|
||||
throw new ServiceException("该编码已存在");
|
||||
}
|
||||
baseMapper.restoreById(existingRailwayStation.getId());
|
||||
railwayStation.setId(existingRailwayStation.getId());
|
||||
railwayStation.setIsDeleted(0);
|
||||
}
|
||||
|
||||
private Map<String, Integer> buildImportValueCountMap(List<String> values) {
|
||||
Map<String, Integer> valueCountMap = new HashMap<>();
|
||||
for (String value : values) {
|
||||
if (Func.isNotEmpty(value)) {
|
||||
valueCountMap.merge(value, 1, Integer::sum);
|
||||
}
|
||||
}
|
||||
return valueCountMap;
|
||||
}
|
||||
|
||||
private void normalizeImportRailwayStation(RailwayStation railwayStation) {
|
||||
railwayStation.setTmisCode(trimToEmpty(railwayStation.getTmisCode()));
|
||||
railwayStation.setCode(CODE_PREFIX + railwayStation.getTmisCode());
|
||||
railwayStation.setTelegraphCode(trimToEmpty(railwayStation.getTelegraphCode()).toUpperCase(Locale.ROOT));
|
||||
railwayStation.setName(trimToEmpty(railwayStation.getName()));
|
||||
railwayStation.setProvinceCode(trimToNull(railwayStation.getProvinceCode()));
|
||||
railwayStation.setProvinceName(trimToNull(railwayStation.getProvinceName()));
|
||||
railwayStation.setCityCode(trimToNull(railwayStation.getCityCode()));
|
||||
railwayStation.setCityName(trimToNull(railwayStation.getCityName()));
|
||||
railwayStation.setDistrictCode(trimToNull(railwayStation.getDistrictCode()));
|
||||
railwayStation.setDistrictName(trimToNull(railwayStation.getDistrictName()));
|
||||
railwayStation.setDetailAddress(trimToNull(railwayStation.getDetailAddress()));
|
||||
railwayStation.setRemark(trimToNull(railwayStation.getRemark()));
|
||||
}
|
||||
|
||||
private BigDecimal parseImportCoordinate(String value, String name, List<String> validationErrors) {
|
||||
String trimValue = trimToEmpty(value);
|
||||
if (trimValue.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return new BigDecimal(trimValue);
|
||||
} catch (NumberFormatException exception) {
|
||||
addValidationError(validationErrors, name + "范围不正确");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> validateImportRailwayStation(RailwayStation railwayStation,
|
||||
Map<String, Integer> tmisCodeCountMap, Map<String, Integer> telegraphCodeCountMap) {
|
||||
List<String> validationErrors = new ArrayList<>();
|
||||
if (Func.isEmpty(railwayStation.getTmisCode())) {
|
||||
addValidationError(validationErrors, "TMIS国标编码不能为空");
|
||||
} else {
|
||||
if (!TMIS_CODE_PATTERN.matcher(railwayStation.getTmisCode()).matches()) {
|
||||
addValidationError(validationErrors, "TMIS国标编码为5位数字");
|
||||
}
|
||||
if (tmisCodeCountMap.getOrDefault(railwayStation.getTmisCode(), 0) > 1) {
|
||||
addValidationError(validationErrors, "TMIS国标编码在本次导入中重复");
|
||||
}
|
||||
validateImportUnique(RailwayStation::getTmisCode, railwayStation.getTmisCode(), "该TMIS国标编码已存在", validationErrors);
|
||||
validateImportUnique(RailwayStation::getCode, railwayStation.getCode(), "该编码已存在", validationErrors);
|
||||
}
|
||||
if (Func.isEmpty(railwayStation.getTelegraphCode())) {
|
||||
addValidationError(validationErrors, "电报略码不能为空");
|
||||
} else {
|
||||
if (!TELEGRAPH_CODE_PATTERN.matcher(railwayStation.getTelegraphCode()).matches()) {
|
||||
addValidationError(validationErrors, "电报略码为3位大写字母");
|
||||
}
|
||||
if (telegraphCodeCountMap.getOrDefault(railwayStation.getTelegraphCode(), 0) > 1) {
|
||||
addValidationError(validationErrors, "电报略码在本次导入中重复");
|
||||
}
|
||||
validateImportUnique(RailwayStation::getTelegraphCode, railwayStation.getTelegraphCode(), "电报略码已存在", validationErrors);
|
||||
}
|
||||
if (Func.isEmpty(railwayStation.getName())) {
|
||||
addValidationError(validationErrors, "车站名称不能为空");
|
||||
}
|
||||
validateImportLength(railwayStation.getCode(), CODE_MAX_LENGTH, "编码不能超过20字", validationErrors);
|
||||
validateImportLength(railwayStation.getName(), NAME_MAX_LENGTH, "车站名称不能超过50字", validationErrors);
|
||||
validateImportLength(railwayStation.getProvinceName(), REGION_NAME_MAX_LENGTH, "所属省份不能超过128字", validationErrors);
|
||||
validateImportLength(railwayStation.getCityName(), REGION_NAME_MAX_LENGTH, "所属城市不能超过128字", validationErrors);
|
||||
validateImportLength(railwayStation.getDistrictName(), REGION_NAME_MAX_LENGTH, "所属区县不能超过128字", validationErrors);
|
||||
if (Func.isEmpty(railwayStation.getDetailAddress())) {
|
||||
addValidationError(validationErrors, "详细地址不能为空");
|
||||
}
|
||||
validateImportLength(railwayStation.getDetailAddress(), DETAIL_ADDRESS_MAX_LENGTH, "详细地址不能超过255字", validationErrors);
|
||||
validateImportLength(railwayStation.getRemark(), REMARK_MAX_LENGTH, "备注不能超过200字", validationErrors);
|
||||
validateImportCoordinate(railwayStation.getLongitude(), MIN_LONGITUDE, MAX_LONGITUDE, "经度", validationErrors);
|
||||
validateImportCoordinate(railwayStation.getLatitude(), MIN_LATITUDE, MAX_LATITUDE, "纬度", validationErrors);
|
||||
validateImportRailwayRegion(railwayStation, validationErrors);
|
||||
return validationErrors;
|
||||
}
|
||||
|
||||
private void validateImportRailwayRegion(RailwayStation railwayStation, List<String> validationErrors) {
|
||||
boolean provinceMissing = Func.isEmpty(railwayStation.getProvinceCode()) && Func.isEmpty(railwayStation.getProvinceName());
|
||||
boolean cityMissing = Func.isEmpty(railwayStation.getCityCode()) && Func.isEmpty(railwayStation.getCityName());
|
||||
boolean districtMissing = Func.isEmpty(railwayStation.getDistrictCode()) && Func.isEmpty(railwayStation.getDistrictName());
|
||||
if (provinceMissing) {
|
||||
addValidationError(validationErrors, "所属省份不能为空");
|
||||
}
|
||||
if (cityMissing) {
|
||||
addValidationError(validationErrors, "所属城市不能为空");
|
||||
}
|
||||
if (districtMissing) {
|
||||
addValidationError(validationErrors, "所属区县不能为空");
|
||||
}
|
||||
if (provinceMissing || cityMissing || districtMissing) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
fillRegion(railwayStation);
|
||||
} catch (ServiceException exception) {
|
||||
addValidationError(validationErrors, exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void validateImportCoordinate(BigDecimal value, BigDecimal min, BigDecimal max, String name, List<String> validationErrors) {
|
||||
if (Func.isEmpty(value)) {
|
||||
addValidationError(validationErrors, name + "不能为空");
|
||||
} else if (!validRange(value, min, max)) {
|
||||
addValidationError(validationErrors, name + "范围不正确");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateImportUnique(com.baomidou.mybatisplus.core.toolkit.support.SFunction<RailwayStation, ?> column,
|
||||
String value, String message, List<String> validationErrors) {
|
||||
if (count(Wrappers.<RailwayStation>lambdaQuery()
|
||||
.eq(column, value)
|
||||
.eq(RailwayStation::getIsDeleted, 0)) > 0L) {
|
||||
addValidationError(validationErrors, message);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateImportLength(String value, int maxLength, String message, List<String> validationErrors) {
|
||||
if (Func.isNotEmpty(value) && value.length() > maxLength) {
|
||||
addValidationError(validationErrors, message);
|
||||
}
|
||||
}
|
||||
|
||||
private void addValidationError(List<String> validationErrors, String message) {
|
||||
if (Func.isNotEmpty(message) && !validationErrors.contains(message)) {
|
||||
validationErrors.add(message);
|
||||
}
|
||||
}
|
||||
|
||||
private String formatImportErrorMessage(List<String> validationErrors) {
|
||||
StringBuilder errorMessage = new StringBuilder();
|
||||
for (int index = 0; index < validationErrors.size(); index++) {
|
||||
if (index > 0) {
|
||||
errorMessage.append(System.lineSeparator());
|
||||
}
|
||||
errorMessage.append(index + 1).append(". ").append(validationErrors.get(index));
|
||||
}
|
||||
return errorMessage.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RailwayStationExcel> exportRailwayStation(Wrapper<RailwayStation> queryWrapper) {
|
||||
public List<RailwayStationExportExcel> exportRailwayStation(Wrapper<RailwayStation> queryWrapper) {
|
||||
List<RailwayStation> railwayStationList = list(queryWrapper);
|
||||
return railwayStationList.stream().map(railwayStation -> {
|
||||
RailwayStationExcel excel = Objects.requireNonNull(BeanUtil.copyProperties(railwayStation, RailwayStationExcel.class));
|
||||
RailwayStationExportExcel excel = Objects.requireNonNull(BeanUtil.copyProperties(railwayStation, RailwayStationExportExcel.class));
|
||||
excel.setLongitude(formatCoordinate(railwayStation.getLongitude(), MIN_LONGITUDE, MAX_LONGITUDE));
|
||||
excel.setLatitude(formatCoordinate(railwayStation.getLatitude(), MIN_LATITUDE, MAX_LATITUDE));
|
||||
excel.setDataSource(normalizeDataSource(railwayStation.getDataSource()));
|
||||
excel.setStatusName(Objects.equals(railwayStation.getStatus(), STATUS_ENABLED) ? "启用" : "停用");
|
||||
excel.setUpdateUserName(UserCache.getUserRealName(railwayStation.getUpdateUser()));
|
||||
return excel;
|
||||
}).toList();
|
||||
}
|
||||
@@ -216,10 +409,10 @@ public class RailwayStationServiceImpl extends BaseServiceImpl<RailwayStationMap
|
||||
throw new ServiceException("TMIS国标编码为5位数字");
|
||||
}
|
||||
if (Func.isEmpty(railwayStation.getTelegraphCode())) {
|
||||
throw new ServiceException("电报码格式不正确或已存在");
|
||||
throw new ServiceException("电报略码格式不正确或已存在");
|
||||
}
|
||||
if (!TELEGRAPH_CODE_PATTERN.matcher(railwayStation.getTelegraphCode()).matches()) {
|
||||
throw new ServiceException("电报码格式不正确或已存在");
|
||||
throw new ServiceException("电报略码格式不正确或已存在");
|
||||
}
|
||||
if (Func.isEmpty(railwayStation.getName())) {
|
||||
throw new ServiceException("请输入车站名称");
|
||||
@@ -243,12 +436,15 @@ public class RailwayStationServiceImpl extends BaseServiceImpl<RailwayStationMap
|
||||
if (Func.isEmpty(railwayStation.getDistrictCode())) {
|
||||
throw new ServiceException("请选择所属区县");
|
||||
}
|
||||
if (Func.isEmpty(railwayStation.getDetailAddress())) {
|
||||
throw new ServiceException("详细地址不能为空");
|
||||
}
|
||||
validateCoordinate(railwayStation.getLongitude(), MIN_LONGITUDE, MAX_LONGITUDE, "经度");
|
||||
validateCoordinate(railwayStation.getLatitude(), MIN_LATITUDE, MAX_LATITUDE, "纬度");
|
||||
validateDataSource(railwayStation.getDataSource());
|
||||
validateStatus(railwayStation.getStatus());
|
||||
validateUnique(railwayStation, RailwayStation::getTmisCode, railwayStation.getTmisCode(), "该TMIS编码已存在");
|
||||
validateUnique(railwayStation, RailwayStation::getTelegraphCode, railwayStation.getTelegraphCode(), "电报码格式不正确或已存在");
|
||||
validateUnique(railwayStation, RailwayStation::getTelegraphCode, railwayStation.getTelegraphCode(), "电报略码格式不正确或已存在");
|
||||
validateUnique(railwayStation, RailwayStation::getCode, railwayStation.getCode(), "该编码已存在");
|
||||
}
|
||||
|
||||
@@ -258,9 +454,10 @@ public class RailwayStationServiceImpl extends BaseServiceImpl<RailwayStationMap
|
||||
province = regionService.getById(railwayStation.getProvinceCode());
|
||||
}
|
||||
if (Func.isEmpty(province) && Func.isNotEmpty(railwayStation.getProvinceName())) {
|
||||
String provinceName = normalizeRegionName(railwayStation.getProvinceName());
|
||||
province = regionService.getOne(Wrappers.<Region>lambdaQuery()
|
||||
.eq(Region::getParentCode, DEFAULT_COUNTRY_CODE)
|
||||
.eq(Region::getName, railwayStation.getProvinceName()), false);
|
||||
.eq(Region::getRegionLevel, PROVINCE_REGION_LEVEL)
|
||||
.eq(Region::getName, provinceName), false);
|
||||
}
|
||||
if (Func.isEmpty(province)) {
|
||||
throw new ServiceException("请选择所属省份");
|
||||
@@ -309,7 +506,7 @@ public class RailwayStationServiceImpl extends BaseServiceImpl<RailwayStationMap
|
||||
|
||||
private void validateCoordinate(BigDecimal value, BigDecimal min, BigDecimal max, String name) {
|
||||
if (value == null) {
|
||||
return;
|
||||
throw new ServiceException(name + "不能为空");
|
||||
}
|
||||
if (value.compareTo(min) < 0 || value.compareTo(max) > 0) {
|
||||
throw new ServiceException(name + "范围不正确");
|
||||
@@ -351,7 +548,11 @@ public class RailwayStationServiceImpl extends BaseServiceImpl<RailwayStationMap
|
||||
}
|
||||
|
||||
private String trimToEmpty(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
return value == null ? "" : value.replace("\uFEFF", "").strip();
|
||||
}
|
||||
|
||||
private String normalizeRegionName(String value) {
|
||||
return trimToEmpty(value);
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
|
||||
+71
-11
@@ -33,14 +33,17 @@ import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.system.pojo.entity.Region;
|
||||
import org.springblade.system.excel.RegionExcel;
|
||||
import org.springblade.system.excel.RegionExportExcel;
|
||||
import org.springblade.system.mapper.RegionMapper;
|
||||
import org.springblade.system.service.IRegionService;
|
||||
import org.springblade.system.pojo.vo.RegionVO;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -57,9 +60,25 @@ public class RegionServiceImpl extends ServiceImpl<RegionMapper, Region> impleme
|
||||
|
||||
@Override
|
||||
public boolean submit(Region region) {
|
||||
Date now = new Date();
|
||||
Long currentUserId = AuthUtil.getUserId();
|
||||
boolean isNew = StringUtil.isBlank(region.getOriginalCode());
|
||||
if (region.getStatus() == null) {
|
||||
region.setStatus(1);
|
||||
}
|
||||
if (StringUtil.isBlank(region.getDataSource())) {
|
||||
region.setDataSource("手动录入");
|
||||
}
|
||||
if (isNew) {
|
||||
region.setCreateUser(currentUserId);
|
||||
region.setCreateTime(now);
|
||||
}
|
||||
region.setUpdateUser(currentUserId);
|
||||
region.setUpdateTime(now);
|
||||
String regionCode = region.getCode();
|
||||
String regionParentCode = region.getParentCode();
|
||||
Integer level = region.getRegionLevel();
|
||||
validateRegionLevel(level);
|
||||
if (level != null && level == COUNTRY_LEVEL) {
|
||||
region.setParentCode(ROOT_PARENT_CODE);
|
||||
region.setAncestors(ROOT_PARENT_CODE);
|
||||
@@ -86,17 +105,29 @@ public class RegionServiceImpl extends ServiceImpl<RegionMapper, Region> impleme
|
||||
region.setAncestors(ancestors);
|
||||
} else if (MAIN_CODE.equals(region.getParentCode())) {
|
||||
region.setAncestors(MAIN_CODE);
|
||||
} else if (ROOT_PARENT_CODE.equals(region.getParentCode())) {
|
||||
region.setAncestors(ROOT_PARENT_CODE);
|
||||
}
|
||||
// 设置省、市、区、镇、村
|
||||
// 设置省、市、区、镇、村,并继承上级区划信息
|
||||
String code = region.getCode();
|
||||
String name = region.getName();
|
||||
if (level == PROVINCE_LEVEL) {
|
||||
region.setProvinceCode(code);
|
||||
region.setProvinceName(name);
|
||||
} else if (level == CITY_LEVEL) {
|
||||
if (Func.isNotEmpty(parent)) {
|
||||
region.setProvinceCode(parent.getProvinceCode());
|
||||
region.setProvinceName(parent.getProvinceName());
|
||||
}
|
||||
region.setCityCode(code);
|
||||
region.setCityName(name);
|
||||
} else if (level == DISTRICT_LEVEL) {
|
||||
if (Func.isNotEmpty(parent)) {
|
||||
region.setProvinceCode(parent.getProvinceCode());
|
||||
region.setProvinceName(parent.getProvinceName());
|
||||
region.setCityCode(parent.getCityCode());
|
||||
region.setCityName(parent.getCityName());
|
||||
}
|
||||
region.setDistrictCode(code);
|
||||
region.setDistrictName(name);
|
||||
} else if (level == TOWN_LEVEL) {
|
||||
@@ -106,7 +137,11 @@ public class RegionServiceImpl extends ServiceImpl<RegionMapper, Region> impleme
|
||||
region.setVillageCode(code);
|
||||
region.setVillageName(name);
|
||||
}
|
||||
return StringUtil.isNotBlank(region.getOriginalCode()) ? this.updateById(region) : this.save(region);
|
||||
boolean result = StringUtil.isNotBlank(region.getOriginalCode()) ? this.updateById(region) : this.save(region);
|
||||
if (result) {
|
||||
clearLazyTree();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void validateUniqueCode(Region region) {
|
||||
@@ -124,13 +159,23 @@ public class RegionServiceImpl extends ServiceImpl<RegionMapper, Region> impleme
|
||||
}
|
||||
}
|
||||
|
||||
private void validateRegionLevel(Integer level) {
|
||||
if (level != null && level > DISTRICT_LEVEL) {
|
||||
throw new ServiceException("区划等级仅支持国家、省份/直辖市、地市、区县");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeRegion(String id) {
|
||||
Long cnt = baseMapper.selectCount(Wrappers.<Region>query().lambda().eq(Region::getParentCode, id));
|
||||
if (cnt > 0L) {
|
||||
throw new ServiceException("请先删除子节点!");
|
||||
}
|
||||
return removeById(id);
|
||||
boolean result = removeById(id);
|
||||
if (result) {
|
||||
clearLazyTree();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -140,7 +185,7 @@ public class RegionServiceImpl extends ServiceImpl<RegionMapper, Region> impleme
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> lazyTree(String parentCode, Map<String, Object> param) {
|
||||
return baseMapper.lazyTree(parentCode, param);
|
||||
return getLazyTree(parentCode, param, () -> baseMapper.lazyTree(parentCode, param));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -149,25 +194,40 @@ public class RegionServiceImpl extends ServiceImpl<RegionMapper, Region> impleme
|
||||
throw new ServiceException("导入数据不能为空");
|
||||
}
|
||||
List<RegionExcel> errorList = new ArrayList<>();
|
||||
boolean cacheChanged = false;
|
||||
for (int index = 0; index < data.size(); index++) {
|
||||
RegionExcel excel = data.get(index);
|
||||
try {
|
||||
Region region = BeanUtil.copyProperties(excel, Region.class);
|
||||
if (Boolean.TRUE.equals(isCovered)) {
|
||||
this.saveOrUpdate(region);
|
||||
} else {
|
||||
this.save(region);
|
||||
validateRegionLevel(region.getRegionLevel());
|
||||
region.setSort(index + 1);
|
||||
if (region.getStatus() == null) {
|
||||
region.setStatus(1);
|
||||
}
|
||||
if (StringUtil.isBlank(region.getDataSource())) {
|
||||
region.setDataSource("初始化导入");
|
||||
}
|
||||
if (Boolean.TRUE.equals(isCovered) && this.getById(region.getCode()) != null) {
|
||||
region.setOriginalCode(region.getCode());
|
||||
}
|
||||
cacheChanged = this.submit(region) || cacheChanged;
|
||||
} catch (Exception exception) {
|
||||
excel.setErrorMessage("第" + (index + 2) + "行:" + exception.getMessage());
|
||||
excel.setErrorMessage(exception.getMessage());
|
||||
errorList.add(excel);
|
||||
}
|
||||
}
|
||||
if (cacheChanged) {
|
||||
clearLazyTree();
|
||||
}
|
||||
return errorList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RegionExcel> exportRegion(Wrapper<Region> queryWrapper) {
|
||||
return baseMapper.exportRegion(queryWrapper);
|
||||
public List<RegionExportExcel> exportRegion(Wrapper<Region> queryWrapper) {
|
||||
List<RegionExportExcel> list = baseMapper.exportRegion(queryWrapper);
|
||||
for (int index = 0; index < list.size(); index++) {
|
||||
list.get(index).setSerialNumber(index + 1);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.cache.utils.CacheUtil;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.redis.cache.BladeRedis;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.resource.feign.ISmsClient;
|
||||
import org.springblade.resource.utils.SmsUtil;
|
||||
import org.springblade.system.pojo.dto.PhoneChangeDTO;
|
||||
import org.springblade.system.pojo.dto.PhoneVerifyDTO;
|
||||
import org.springblade.system.pojo.entity.User;
|
||||
import org.springblade.system.service.IUserPhoneService;
|
||||
import org.springblade.system.service.IUserService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.springblade.core.cache.constant.CacheConstant.USER_CACHE;
|
||||
|
||||
/**
|
||||
* 用户手机号变更服务实现
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserPhoneServiceImpl implements IUserPhoneService {
|
||||
|
||||
/**
|
||||
* 与登录短信一致,对应后台 /resource/sms 的 smsCode
|
||||
*/
|
||||
private static final String SMS_RESOURCE_CODE = "ali_reg";
|
||||
|
||||
/**
|
||||
* 原手机号已校验凭证(Redis)
|
||||
*/
|
||||
private static final String PHONE_CHANGE_VERIFIED_KEY = "blade:user:phone:change:verified:";
|
||||
|
||||
private static final Duration PHONE_CHANGE_VERIFIED_TTL = Duration.ofMinutes(15);
|
||||
|
||||
private static final Pattern MOBILE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
|
||||
|
||||
private final IUserService userService;
|
||||
private final ISmsClient smsClient;
|
||||
private final BladeRedis bladeRedis;
|
||||
|
||||
@Override
|
||||
public R sendCode(String phone) {
|
||||
String normalizedPhone = normalizePhone(phone);
|
||||
Long userId = AuthUtil.getUserId();
|
||||
if (Func.isEmpty(userId)) {
|
||||
throw new ServiceException("请先登录");
|
||||
}
|
||||
User currentUser = requireCurrentUser(userId);
|
||||
String tenantId = Func.toStr(currentUser.getTenantId(), AuthUtil.getTenantId());
|
||||
boolean isCurrentPhone = StringUtil.equals(normalizedPhone, Func.toStr(currentUser.getPhone()));
|
||||
if (!isCurrentPhone) {
|
||||
assertPhoneAvailable(tenantId, normalizedPhone, userId);
|
||||
}
|
||||
R result = smsClient.sendValidate(tenantId, SMS_RESOURCE_CODE, normalizedPhone);
|
||||
if (result == null || !result.isSuccess()) {
|
||||
return R.fail(SmsUtil.SEND_FAIL);
|
||||
}
|
||||
return R.data(result.getData(), SmsUtil.SEND_SUCCESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verifyOldPhone(PhoneVerifyDTO phoneVerify) {
|
||||
Long userId = AuthUtil.getUserId();
|
||||
if (Func.isEmpty(userId)) {
|
||||
throw new ServiceException("请先登录");
|
||||
}
|
||||
String id = Func.toStr(phoneVerify.getId()).trim();
|
||||
String code = Func.toStr(phoneVerify.getCode()).trim();
|
||||
if (StringUtil.isBlank(id) || StringUtil.isBlank(code)) {
|
||||
throw new ServiceException("请先获取并填写验证码");
|
||||
}
|
||||
User currentUser = requireCurrentUser(userId);
|
||||
String oldPhone = Func.toStr(currentUser.getPhone()).trim();
|
||||
if (StringUtil.isBlank(oldPhone)) {
|
||||
throw new ServiceException("当前账号未绑定手机号");
|
||||
}
|
||||
validateSms(currentUser.getTenantId(), id, code, oldPhone);
|
||||
bladeRedis.setEx(PHONE_CHANGE_VERIFIED_KEY + userId, "1", PHONE_CHANGE_VERIFIED_TTL);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean changePhone(PhoneChangeDTO phoneChange) {
|
||||
Long userId = AuthUtil.getUserId();
|
||||
if (Func.isEmpty(userId)) {
|
||||
throw new ServiceException("请先登录");
|
||||
}
|
||||
String verified = Func.toStr(bladeRedis.get(PHONE_CHANGE_VERIFIED_KEY + userId));
|
||||
if (!StringUtil.equals(verified, "1")) {
|
||||
throw new ServiceException("请先完成原手机号验证");
|
||||
}
|
||||
String id = Func.toStr(phoneChange.getId()).trim();
|
||||
String code = Func.toStr(phoneChange.getCode()).trim();
|
||||
String newPhone = normalizePhone(phoneChange.getNewPhone());
|
||||
if (StringUtil.isBlank(id) || StringUtil.isBlank(code)) {
|
||||
throw new ServiceException("请先获取并填写验证码");
|
||||
}
|
||||
User currentUser = requireCurrentUser(userId);
|
||||
String oldPhone = Func.toStr(currentUser.getPhone()).trim();
|
||||
if (StringUtil.equals(newPhone, oldPhone)) {
|
||||
throw new ServiceException("新手机号不可与当前手机号相同");
|
||||
}
|
||||
String tenantId = Func.toStr(currentUser.getTenantId(), AuthUtil.getTenantId());
|
||||
assertPhoneAvailable(tenantId, newPhone, userId);
|
||||
validateSms(tenantId, id, code, newPhone);
|
||||
|
||||
User updateUser = new User();
|
||||
updateUser.setId(userId);
|
||||
updateUser.setPhone(newPhone);
|
||||
// 账号若等于原手机号,同步更新,保证短信登录可用
|
||||
if (StringUtil.isNotBlank(oldPhone) && StringUtil.equals(oldPhone, Func.toStr(currentUser.getAccount()))) {
|
||||
assertAccountAvailable(tenantId, newPhone, userId);
|
||||
updateUser.setAccount(newPhone);
|
||||
}
|
||||
boolean updated = userService.updateById(updateUser);
|
||||
if (!updated) {
|
||||
throw new ServiceException("手机号修改失败");
|
||||
}
|
||||
bladeRedis.del(PHONE_CHANGE_VERIFIED_KEY + userId);
|
||||
CacheUtil.clear(USER_CACHE);
|
||||
return true;
|
||||
}
|
||||
|
||||
private User requireCurrentUser(Long userId) {
|
||||
User user = userService.getById(userId);
|
||||
if (user == null) {
|
||||
throw new ServiceException("用户不存在");
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private void validateSms(String tenantId, String id, String value, String phone) {
|
||||
R result = smsClient.validateMessage(tenantId, SMS_RESOURCE_CODE, id, value, phone);
|
||||
if (result == null || !result.isSuccess()) {
|
||||
throw new ServiceException(SmsUtil.VALIDATE_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertPhoneAvailable(String tenantId, String phone, Long excludeUserId) {
|
||||
Long phoneCount = userService.count(
|
||||
Wrappers.<User>lambdaQuery()
|
||||
.eq(User::getTenantId, tenantId)
|
||||
.eq(User::getPhone, phone)
|
||||
.ne(User::getId, excludeUserId)
|
||||
);
|
||||
if (phoneCount != null && phoneCount > 0L) {
|
||||
throw new ServiceException(StringUtil.format("当前手机 [{}] 已存在!", phone));
|
||||
}
|
||||
}
|
||||
|
||||
private void assertAccountAvailable(String tenantId, String account, Long excludeUserId) {
|
||||
Long accountCount = userService.count(
|
||||
Wrappers.<User>lambdaQuery()
|
||||
.eq(User::getTenantId, tenantId)
|
||||
.eq(User::getAccount, account)
|
||||
.ne(User::getId, excludeUserId)
|
||||
);
|
||||
if (accountCount != null && accountCount > 0L) {
|
||||
throw new ServiceException(StringUtil.format("当前用户 [{}] 已存在!", account));
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizePhone(String phone) {
|
||||
String normalizedPhone = Func.toStr(phone).trim();
|
||||
if (!MOBILE_PATTERN.matcher(normalizedPhone).matches()) {
|
||||
throw new ServiceException("手机号格式不正确");
|
||||
}
|
||||
return normalizedPhone;
|
||||
}
|
||||
|
||||
}
|
||||
+290
@@ -31,7 +31,11 @@ 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.Wrappers;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.common.constant.DataStatusEnum;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.springblade.common.constant.ParamConstant;
|
||||
import org.springblade.common.constant.TenantConstant;
|
||||
@@ -63,6 +67,7 @@ import org.springblade.system.pojo.entity.*;
|
||||
import org.springblade.system.pojo.enums.DictEnum;
|
||||
import org.springblade.system.pojo.enums.UserType;
|
||||
import org.springblade.system.pojo.vo.UserVO;
|
||||
import org.springblade.system.props.IamSyncProperties;
|
||||
import org.springblade.system.service.IRoleService;
|
||||
import org.springblade.system.service.IUserDeptService;
|
||||
import org.springblade.system.service.IUserOauthService;
|
||||
@@ -72,12 +77,20 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
|
||||
import static org.springblade.common.constant.ParamConstant.DEFAULT_PARAM_PASSWORD;
|
||||
import static org.springblade.core.cache.constant.CacheConstant.USER_CACHE;
|
||||
@@ -90,11 +103,17 @@ import static org.springblade.core.tenant.TenantGuard.EntityType.USER;
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements IUserService {
|
||||
private static final String GUEST_NAME = "guest";
|
||||
private static final String PASSWORD_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
|
||||
private static final int RANDOM_PASSWORD_LENGTH = 8;
|
||||
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||
private static final String IAM_DEFAULT_DICT_CODE = "iam_default";
|
||||
private static final String IAM_DEFAULT_ROLE_NAME = "默认角色";
|
||||
private static final String IAM_DEFAULT_DEPT_NAME = "默认部门";
|
||||
private static final String IAM_SYNC_TENANT_ID = "000000";
|
||||
private static final HttpClient IAM_HTTP_CLIENT = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
|
||||
|
||||
private final IUserDeptService userDeptService;
|
||||
private final UserDataScopeMapper userDataScopeMapper;
|
||||
@@ -104,6 +123,8 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
|
||||
private final BladeTenantProperties tenantProperties;
|
||||
|
||||
private final OAuth2Properties properties;
|
||||
private final IamSyncProperties iamSyncProperties;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
|
||||
@Override
|
||||
@@ -131,6 +152,163 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
|
||||
return saveUser(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int syncIamAccounts() {
|
||||
int pageNumber = 1;
|
||||
int fetchedCount = 0;
|
||||
int syncedCount = 0;
|
||||
int totalCount = -1;
|
||||
int pageSize = iamSyncProperties.getPageSize() > 0 ? iamSyncProperties.getPageSize() : 50;
|
||||
while (true) {
|
||||
JsonNode dataNode = requestIamAccountPage(pageNumber, pageSize);
|
||||
JsonNode accountList = dataNode.path("list");
|
||||
if (!accountList.isArray() || accountList.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
if (dataNode.has("total")) {
|
||||
totalCount = dataNode.path("total").asInt(totalCount);
|
||||
}
|
||||
for (JsonNode accountNode : accountList) {
|
||||
if (syncIamAccount(accountNode)) {
|
||||
syncedCount++;
|
||||
}
|
||||
}
|
||||
fetchedCount += accountList.size();
|
||||
int responsePage = dataNode.path("page").asInt(pageNumber);
|
||||
int responseSize = dataNode.path("size").asInt(pageSize);
|
||||
if ((totalCount >= 0 && fetchedCount >= totalCount)
|
||||
|| accountList.size() < pageSize
|
||||
|| (totalCount >= 0 && responsePage * responseSize >= totalCount)) {
|
||||
break;
|
||||
}
|
||||
pageNumber = responsePage + 1;
|
||||
}
|
||||
log.info("IAM账号同步完成,tenantId={}, fetchedCount={}, syncedCount={}", IAM_SYNC_TENANT_ID, fetchedCount, syncedCount);
|
||||
return syncedCount;
|
||||
}
|
||||
|
||||
private JsonNode requestIamAccountPage(int pageNumber, int pageSize) {
|
||||
try {
|
||||
Map<String, String> requestBody = new LinkedHashMap<>();
|
||||
requestBody.put("size", String.valueOf(pageSize));
|
||||
requestBody.put("page", String.valueOf(pageNumber));
|
||||
HttpRequest request = HttpRequest.newBuilder(URI.create(iamSyncProperties.getAccountListUrl()))
|
||||
.timeout(Duration.ofSeconds(20))
|
||||
.header("Accept", "application/json")
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Auth", normalizeAuthorizationHeader(iamSyncProperties.getProfileAuthorization()))
|
||||
.header("Authorization", normalizeAuthorizationHeader(iamSyncProperties.getAuthorization()))
|
||||
.POST(HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(requestBody), StandardCharsets.UTF_8))
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = IAM_HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||
if (response.statusCode() < 200 || response.statusCode() >= 300) {
|
||||
throw new ServiceException(StringUtil.format("IAM账号接口调用失败,HTTP状态码:{}", response.statusCode()));
|
||||
}
|
||||
JsonNode responseNode = objectMapper.readTree(response.body());
|
||||
if (!"0".equals(responseNode.path("code").asText())) {
|
||||
throw new ServiceException(StringUtil.format("IAM账号接口调用失败:{}", responseNode.path("msg").asText()));
|
||||
}
|
||||
JsonNode dataNode = responseNode.path("data");
|
||||
if (!dataNode.isObject()) {
|
||||
throw new ServiceException("IAM账号接口返回数据格式错误");
|
||||
}
|
||||
return dataNode;
|
||||
} catch (InterruptedException exception) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.error("调用IAM账号接口被中断,page={}", pageNumber, exception);
|
||||
throw new ServiceException("调用IAM账号接口被中断");
|
||||
} catch (IOException | IllegalArgumentException exception) {
|
||||
log.error("调用IAM账号接口失败,page={}", pageNumber, exception);
|
||||
throw new ServiceException("调用IAM账号接口失败");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean syncIamAccount(JsonNode accountNode) {
|
||||
String account = readIamText(accountNode, "accountNo", "account_no", "app_account__account_no");
|
||||
if (StringUtil.isBlank(account)) {
|
||||
log.warn("IAM账号缺少accountNo,跳过同步");
|
||||
return false;
|
||||
}
|
||||
String name = readIamText(accountNode, "name", "app_account__name");
|
||||
if (StringUtil.isBlank(name)) {
|
||||
name = readIamText(accountNode, "accountName", "account_name", "app_account__account_name");
|
||||
}
|
||||
if (StringUtil.isBlank(name)) {
|
||||
name = account;
|
||||
}
|
||||
Integer status = readIamInt(accountNode, "status", "app_account__status") == 1
|
||||
? DataStatusEnum.ENABLE.getCode() : DataStatusEnum.DISABLE.getCode();
|
||||
User user = userByAccount(IAM_SYNC_TENANT_ID, account);
|
||||
if (user == null) {
|
||||
user = new User();
|
||||
user.setTenantId(IAM_SYNC_TENANT_ID);
|
||||
user.setAccount(account);
|
||||
user.setName(name);
|
||||
user.setRealName(name);
|
||||
user.setRoleId(resolveIamDefaultRoleId());
|
||||
user.setDeptId(resolveIamDefaultId(IAM_DEFAULT_DEPT_NAME));
|
||||
user.setPostId(StringPool.MINUS_ONE);
|
||||
user.setUserType(UserType.WEB.getCategory());
|
||||
user.setStatus(status);
|
||||
user.setIsOa(1);
|
||||
user.setSyncTime(new Date());
|
||||
applyUserDefaults(user);
|
||||
return saveUser(user);
|
||||
}
|
||||
boolean changed = !Objects.equals(user.getName(), name) || !Objects.equals(user.getRealName(), name)
|
||||
|| !Objects.equals(user.getStatus(), status) || !Objects.equals(user.getIsOa(), 1);
|
||||
if (isMissingIamRole(user.getRoleId())) {
|
||||
user.setRoleId(resolveIamDefaultRoleId());
|
||||
changed = true;
|
||||
}
|
||||
if (isMissingIamAssignment(user.getDeptId())) {
|
||||
user.setDeptId(resolveIamDefaultId(IAM_DEFAULT_DEPT_NAME));
|
||||
changed = true;
|
||||
}
|
||||
if (!changed) {
|
||||
return true;
|
||||
}
|
||||
user.setName(name);
|
||||
user.setRealName(name);
|
||||
user.setStatus(status);
|
||||
user.setIsOa(1);
|
||||
user.setSyncTime(new Date());
|
||||
CacheUtil.clear(USER_CACHE);
|
||||
return updateById(user);
|
||||
}
|
||||
|
||||
private String readIamText(JsonNode node, String... fieldNames) {
|
||||
JsonNode valueNode = findIamNode(node, fieldNames);
|
||||
return valueNode == null || valueNode.isNull() ? StringPool.EMPTY : valueNode.asText().trim();
|
||||
}
|
||||
|
||||
private int readIamInt(JsonNode node, String... fieldNames) {
|
||||
JsonNode valueNode = findIamNode(node, fieldNames);
|
||||
return valueNode == null || valueNode.isNull() ? 0 : valueNode.asInt(0);
|
||||
}
|
||||
|
||||
private JsonNode findIamNode(JsonNode node, String... fieldNames) {
|
||||
for (String fieldName : fieldNames) {
|
||||
JsonNode valueNode = node.get(fieldName);
|
||||
if (valueNode != null && !valueNode.isNull()) {
|
||||
return valueNode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String normalizeAuthorizationHeader(String value) {
|
||||
if (StringUtil.isBlank(value)) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
if (StringUtil.startsWithIgnoreCase(value, "Basic ") || StringUtil.startsWithIgnoreCase(value, "Bearer ")) {
|
||||
return value;
|
||||
}
|
||||
return "Basic " + value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean updateUser(User user) {
|
||||
@@ -351,6 +529,46 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean bindWxMiniOpenId(String tenantId, Long userId, String openid, String phone) {
|
||||
if (Func.isBlank(tenantId) || Func.isEmpty(userId) || Func.isBlank(openid)) {
|
||||
throw new ServiceException("绑定微信 openid 参数不完整");
|
||||
}
|
||||
String source = "WECHAT_MINI";
|
||||
UserOauth byOpenId = userOauthService.getOne(Wrappers.<UserOauth>lambdaQuery()
|
||||
.eq(UserOauth::getTenantId, tenantId)
|
||||
.eq(UserOauth::getSource, source)
|
||||
.eq(UserOauth::getUuid, openid)
|
||||
.last("LIMIT 1"));
|
||||
if (byOpenId != null) {
|
||||
byOpenId.setUserId(userId);
|
||||
if (Func.isNotBlank(phone)) {
|
||||
byOpenId.setUsername(phone);
|
||||
}
|
||||
return userOauthService.updateById(byOpenId);
|
||||
}
|
||||
UserOauth byUser = userOauthService.getOne(Wrappers.<UserOauth>lambdaQuery()
|
||||
.eq(UserOauth::getTenantId, tenantId)
|
||||
.eq(UserOauth::getSource, source)
|
||||
.eq(UserOauth::getUserId, userId)
|
||||
.last("LIMIT 1"));
|
||||
if (byUser != null) {
|
||||
byUser.setUuid(openid);
|
||||
if (Func.isNotBlank(phone)) {
|
||||
byUser.setUsername(phone);
|
||||
}
|
||||
return userOauthService.updateById(byUser);
|
||||
}
|
||||
UserOauth oauth = new UserOauth();
|
||||
oauth.setTenantId(tenantId);
|
||||
oauth.setUserId(userId);
|
||||
oauth.setUuid(openid);
|
||||
oauth.setUsername(Func.toStr(phone, ""));
|
||||
oauth.setSource(source);
|
||||
return userOauthService.save(oauth);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean grant(String userIds, String roleIds) {
|
||||
@@ -549,6 +767,78 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implement
|
||||
return saveUser(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean saveIamUser(User user) {
|
||||
if (AuthUtil.hasAuth()) {
|
||||
throw new ServiceException("IAM用户创建仅允许统一身份认证流程调用!");
|
||||
}
|
||||
Tenant tenant = SysCache.getTenant(user.getTenantId());
|
||||
if (tenant == null || tenant.getId() == null) {
|
||||
throw new ServiceException("租户信息错误!");
|
||||
}
|
||||
if (user.getUserType() == null) {
|
||||
user.setUserType(UserType.WEB.getCategory());
|
||||
}
|
||||
String defaultRoleId = resolveIamDefaultRoleId();
|
||||
User existingUser = userByAccount(user.getTenantId(), user.getAccount());
|
||||
if (existingUser != null) {
|
||||
boolean changed = false;
|
||||
if (isMissingIamRole(existingUser.getRoleId())) {
|
||||
existingUser.setRoleId(defaultRoleId);
|
||||
changed = true;
|
||||
}
|
||||
if (isMissingIamAssignment(existingUser.getDeptId())) {
|
||||
existingUser.setDeptId(resolveIamDefaultId(IAM_DEFAULT_DEPT_NAME));
|
||||
changed = true;
|
||||
}
|
||||
if (existingUser.getIsOa() == null || existingUser.getIsOa() != 1) {
|
||||
existingUser.setIsOa(1);
|
||||
existingUser.setSyncTime(new Date());
|
||||
changed = true;
|
||||
}
|
||||
if (!changed) {
|
||||
return true;
|
||||
}
|
||||
CacheUtil.clear(USER_CACHE);
|
||||
return this.updateById(existingUser);
|
||||
}
|
||||
if (isMissingIamRole(user.getRoleId())) {
|
||||
user.setRoleId(defaultRoleId);
|
||||
}
|
||||
if (StringUtil.isBlank(user.getDeptId())) {
|
||||
user.setDeptId(StringPool.MINUS_ONE);
|
||||
}
|
||||
if (StringUtil.isBlank(user.getPostId())) {
|
||||
user.setPostId(StringPool.MINUS_ONE);
|
||||
}
|
||||
user.setIsOa(1);
|
||||
user.setSyncTime(new Date());
|
||||
user.setStatus(StatusType.ACTIVE.getType());
|
||||
applyUserDefaults(user);
|
||||
return saveUser(user);
|
||||
}
|
||||
|
||||
private boolean isMissingIamRole(String roleId) {
|
||||
return StringUtil.isBlank(roleId) || StringPool.MINUS_ONE.equals(roleId);
|
||||
}
|
||||
|
||||
private String resolveIamDefaultRoleId() {
|
||||
return resolveIamDefaultId(IAM_DEFAULT_ROLE_NAME);
|
||||
}
|
||||
|
||||
private String resolveIamDefaultId(String dictValue) {
|
||||
String dictKey = DictCache.getKey(IAM_DEFAULT_DICT_CODE, dictValue);
|
||||
if (StringUtil.isBlank(dictKey) || StringPool.MINUS_ONE.equals(dictKey)) {
|
||||
throw new ServiceException(StringUtil.format("IAM默认配置 [{}] 未配置有效键值", dictValue));
|
||||
}
|
||||
return dictKey;
|
||||
}
|
||||
|
||||
private boolean isMissingIamAssignment(String value) {
|
||||
return StringUtil.isBlank(value) || StringPool.MINUS_ONE.equals(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean updatePlatform(Long userId, Integer userType, String userExt) {
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments from this software for such purposes. Copyright of this software remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.wrapper;
|
||||
|
||||
import org.springblade.core.mp.support.BaseEntityWrapper;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.cache.SysCache;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.system.pojo.entity.InvoiceItem;
|
||||
import org.springblade.system.pojo.vo.InvoiceItemVO;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 开票项目包装类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class InvoiceItemWrapper extends BaseEntityWrapper<InvoiceItem, InvoiceItemVO> {
|
||||
|
||||
public static InvoiceItemWrapper build() {
|
||||
return new InvoiceItemWrapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvoiceItemVO entityVO(InvoiceItem invoiceItem) {
|
||||
InvoiceItemVO vo = Objects.requireNonNull(BeanUtil.copyProperties(invoiceItem, InvoiceItemVO.class));
|
||||
vo.setCreateDeptName(Func.isEmpty(invoiceItem.getCreateDept()) ? "" : SysCache.getDeptName(invoiceItem.getCreateDept()));
|
||||
vo.setUpdateUserName(UserCache.getUserRealName(invoiceItem.getUpdateUser()));
|
||||
return vo;
|
||||
}
|
||||
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.system.wrapper;
|
||||
|
||||
import org.springblade.core.mp.support.BaseEntityWrapper;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.system.pojo.entity.MeasurementUnit;
|
||||
import org.springblade.system.pojo.vo.MeasurementUnitVO;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 计量单位包装类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class MeasurementUnitWrapper extends BaseEntityWrapper<MeasurementUnit, MeasurementUnitVO> {
|
||||
|
||||
public static MeasurementUnitWrapper build() {
|
||||
return new MeasurementUnitWrapper();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MeasurementUnitVO entityVO(MeasurementUnit measurementUnit) {
|
||||
MeasurementUnitVO measurementUnitVO = Objects.requireNonNull(
|
||||
BeanUtil.copyProperties(measurementUnit, MeasurementUnitVO.class)
|
||||
);
|
||||
measurementUnitVO.setCreateUserName(UserCache.getUserRealName(measurementUnit.getCreateUser()));
|
||||
measurementUnitVO.setUpdateUserName(UserCache.getUserRealName(measurementUnit.getUpdateUser()));
|
||||
return measurementUnitVO;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user