1、调整导入运单

2、调整对账
This commit is contained in:
2026-09-10 22:50:00 +08:00
parent e7d7e61846
commit 93c7e6f8f5
33 changed files with 553 additions and 22 deletions
@@ -64,6 +64,7 @@ public interface ISysClient {
String ROLE_NAMES = API_PREFIX + "/role-names";
String ROLE_ALIAS = API_PREFIX + "/role-alias";
String ROLE_ALIASES = API_PREFIX + "/role-aliases";
String ROLE_ID_BY_ALIAS = API_PREFIX + "/role-id-by-alias";
String TENANT = API_PREFIX + "/tenant";
String TENANT_ID = API_PREFIX + "/tenant-id";
String TENANT_PACKAGE = API_PREFIX + "/tenant-package";
@@ -240,6 +241,16 @@ public interface ISysClient {
@GetMapping(ROLE_ALIASES)
R<List<String>> getRoleAliases(@RequestParam("roleIds") String roleIds);
/**
* 根据角色别名获取角色id
*
* @param tenantId 租户id
* @param roleAlias 角色别名
* @return 角色id
*/
@GetMapping(ROLE_ID_BY_ALIAS)
R<String> getRoleIdByAlias(@RequestParam("tenantId") String tenantId, @RequestParam("roleAlias") String roleAlias);
/**
* 获取租户
*
@@ -129,6 +129,11 @@ public class ISysClientFallback implements ISysClient {
return R.fail("获取数据失败");
}
@Override
public R<String> getRoleIdByAlias(String tenantId, String roleAlias) {
return R.fail("获取数据失败");
}
@Override
public R<Tenant> getTenant(Long id) {
return R.fail("获取数据失败");
@@ -191,6 +191,11 @@ public class Driver extends TenantEntity {
*/
@Schema(description = "手机号")
private String mobile;
/**
* 关联系统用户ID
*/
@Schema(description = "关联系统用户ID")
private Long userId;
/**
* 与联系人关系
*/
@@ -118,6 +118,9 @@ public class ProjectApply extends TenantEntity {
@Schema(description = "业务类型")
private String businessType;
@Schema(description = "业务模式")
private String businessMode;
@Schema(description = "项目规模(万元)")
@TableField(insertStrategy = FieldStrategy.ALWAYS, updateStrategy = FieldStrategy.ALWAYS)
private BigDecimal projectScale;
@@ -126,6 +129,10 @@ public class ProjectApply extends TenantEntity {
@TableField(insertStrategy = FieldStrategy.ALWAYS, updateStrategy = FieldStrategy.ALWAYS)
private BigDecimal estimatedProfit;
@Schema(description = "利润率(%)")
@TableField(insertStrategy = FieldStrategy.ALWAYS, updateStrategy = FieldStrategy.ALWAYS)
private BigDecimal profitRate;
@Schema(description = "资金需求(万元)")
@TableField(insertStrategy = FieldStrategy.ALWAYS, updateStrategy = FieldStrategy.ALWAYS)
private BigDecimal fundDemand;
@@ -53,6 +53,11 @@ public class TransportVehicle extends TenantEntity {
*/
@Schema(description = "所属组织")
private String organizationName;
/**
* 使用部门
*/
@Schema(description = "使用部门")
private String useDepartment;
/**
* 车牌号
*/
@@ -69,4 +69,11 @@ public class TransportVehicleVO extends TransportVehicle {
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate warningDate;
/**
* 绑定司机(按车牌反查司机驾驶车辆,多个用顿号拼接)
*/
@TableField(exist = false)
@Schema(description = "绑定司机")
private String boundDriver;
}
@@ -57,6 +57,7 @@ public interface IUserClient {
String USER_BY_ACCOUNT = API_PREFIX + "/user-by-account";
String USER_AUTH_INFO = API_PREFIX + "/user-auth-info";
String SAVE_USER = API_PREFIX + "/save-user";
String UPDATE_USER = API_PREFIX + "/update-user";
String SAVE_IAM_USER = API_PREFIX + "/save-iam-user";
String REGISTER_USER = API_PREFIX + "/register-user";
String REMOVE_USER = API_PREFIX + "/remove-user";
@@ -150,6 +151,15 @@ public interface IUserClient {
@PostMapping(SAVE_USER)
R<Boolean> saveUser(@RequestBody User user);
/**
* 更新用户
*
* @param user 用户实体
* @return
*/
@PostMapping(UPDATE_USER)
R<Boolean> updateUser(@RequestBody User user);
/**
* 新建IAM用户
*
@@ -167,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) {
@@ -108,6 +108,12 @@ 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) {
@@ -23,6 +23,7 @@ import org.springblade.core.excel.util.ExcelUtil;
import org.springblade.transport.excel.CargoReconciliationExcel;
import org.springblade.transport.excel.CargoReconciliationFeeReader;
import org.springblade.transport.excel.CargoReconciliationFailureExcel;
import org.springblade.transport.excel.ReconciliationImportTemplateExcel;
import org.springblade.transport.excel.VehicleReconciliationExcel;
import org.springblade.transport.excel.VehicleReconciliationFeeReader;
import org.springblade.transport.excel.VehicleReconciliationFailureExcel;
@@ -154,9 +155,12 @@ public class TransportReconciliationController extends BladeController {
@GetMapping("/template")
@ApiOperationSupport(order = 8)
@Operation(summary = "下载运输对账模板")
public void template(@RequestParam String mode, HttpServletResponse response) {
if ("cargo".equals(mode)) ExcelUtil.export(response, "货物明细对账模板", "货物明细对账模板", new ArrayList<CargoReconciliationExcel>(), CargoReconciliationExcel.class);
else ExcelUtil.export(response, "整车总额对账模板", "整车总额对账模板", new ArrayList<VehicleReconciliationExcel>(), VehicleReconciliationExcel.class);
public void template(@RequestParam String mode, @RequestParam(required = false) Long id,
@RequestParam(required = false) Long formalSettlementId, @RequestParam(required = false) String feeItems,
HttpServletResponse response) {
List<String> extraFeeItems = reconciliationService.templateFeeItems(id, formalSettlementId, feeItems);
if ("cargo".equals(mode)) ReconciliationImportTemplateExcel.exportCargo(response, extraFeeItems);
else ReconciliationImportTemplateExcel.exportVehicle(response, extraFeeItems);
}
@PostMapping("/match")
@@ -131,8 +131,15 @@ public class WaybillController extends BladeController {
return R.data(options);
}
@GetMapping("/import-batch/list")
@GetMapping("/import-batch/next-code")
@ApiOperationSupport(order = 4)
@Operation(summary = "下一个运单批次号")
public R<String> importBatchNextCode() {
return R.data(waybillImportBatchService.nextBatchNo());
}
@GetMapping("/import-batch/list")
@ApiOperationSupport(order = 5)
@Operation(summary = "运单批量导入批次分页")
public R<IPage<WaybillImportBatchVO>> importBatchList(WaybillImportBatchRequest request, Query query) {
return R.data(waybillImportBatchService.page(Condition.getPage(query), request));
@@ -35,8 +35,8 @@ public class CargoReconciliationExcel implements Serializable {
@ExcelProperty("运输单价") @NumberFormat("0.00") private BigDecimal unitPrice;
@ExcelProperty("里程(KM") @NumberFormat("0.00") private BigDecimal mileage;
@ExcelProperty("运输费") @NumberFormat("0.00") private BigDecimal freightAmount;
@ExcelProperty("水费") @NumberFormat("0.00") private BigDecimal feeItemOne;
@ExcelProperty("罚款") @NumberFormat("0.00") private BigDecimal feeItemTwo;
@ExcelIgnore @NumberFormat("0.00") private BigDecimal feeItemOne;
@ExcelIgnore @NumberFormat("0.00") private BigDecimal feeItemTwo;
@ExcelProperty("结算费用合计") @NumberFormat("0.00") private BigDecimal settlementAmount;
@ExcelIgnore private Map<String, BigDecimal> feeItems;
@ExcelIgnore private String errorMessage;
@@ -86,10 +86,14 @@ public class ProjectApplyExcel implements Serializable {
private String transportType;
@ExcelProperty("业务类型")
private String businessType;
@ExcelProperty("业务模式")
private String businessMode;
@ExcelProperty("项目规模(万元)")
private BigDecimal projectScale;
@ExcelProperty("预计利润(万元)")
private BigDecimal estimatedProfit;
@ExcelProperty("利润率(%)")
private BigDecimal profitRate;
@ExcelProperty("资金需求(万元)")
private BigDecimal fundDemand;
@ExcelProperty("结算方式")
@@ -0,0 +1,71 @@
/**
* 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>
* <p>Author: Chill Zhuang (bladejava@qq.com)</p>
*/
package org.springblade.transport.excel;
import cn.idev.excel.FastExcel;
import cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/** 运输对账导入模板导出。费用列按当前内部账单收费项动态生成。 */
public final class ReconciliationImportTemplateExcel {
private static final List<String> VEHICLE_HEADERS = List.of(
"车牌号", "发货地址", "到货地址", "实际发货时间", "实际完成时间", "运输类型",
"货物名称", "货物类型", "运输总量", "里程(KM", "批次号", "运输单价", "运输费"
);
private static final List<String> CARGO_HEADERS = List.of(
"车牌号", "发货地址", "到货地址", "实际发货时间", "实际完成时间", "货物名称",
"货物类型", "规格", "型号", "运输总量", "运输单价", "里程(KM", "运输费"
);
private static final String SETTLEMENT_AMOUNT = "结算费用合计";
private ReconciliationImportTemplateExcel() {
}
public static void exportVehicle(HttpServletResponse response, List<String> extraFeeItems) {
export(response, "整车总额对账模板", VEHICLE_HEADERS, extraFeeItems);
}
public static void exportCargo(HttpServletResponse response, List<String> extraFeeItems) {
export(response, "货物明细对账模板", CARGO_HEADERS, extraFeeItems);
}
private static void export(HttpServletResponse response, String fileName, List<String> baseHeaders,
List<String> extraFeeItems) {
List<List<String>> head = new ArrayList<>();
for (String header : baseHeaders) {
head.add(List.of(header));
}
if (extraFeeItems != null) {
for (String feeItem : extraFeeItems) {
if (feeItem != null && !feeItem.isBlank()) {
head.add(List.of(feeItem.trim()));
}
}
}
head.add(List.of(SETTLEMENT_AMOUNT));
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setHeader("Content-disposition",
"attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8) + ".xlsx");
try {
FastExcel.write(response.getOutputStream())
.head(head)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.sheet(fileName)
.doWrite(List.of());
} catch (IOException exception) {
throw new IllegalStateException("导出" + fileName + "失败", exception);
}
}
}
@@ -59,6 +59,9 @@ public class TransportVehicleExcel implements Serializable {
@ExcelProperty("所属组织 *")
private String organizationName;
@ExcelProperty("使用部门")
private String useDepartment;
@ExcelProperty("业务关系 *")
private String businessRelation;
@@ -35,7 +35,7 @@ public class VehicleReconciliationExcel implements Serializable {
@ExcelProperty("批次号") private String batchNo;
@ExcelProperty("运输单价") @NumberFormat("0.00") private BigDecimal unitPrice;
@ExcelProperty("运输费") @NumberFormat("0.00") private BigDecimal freightAmount;
@ExcelProperty("费用项目1") @NumberFormat("0.00") private BigDecimal feeItemOne;
@ExcelIgnore @NumberFormat("0.00") private BigDecimal feeItemOne;
@ExcelProperty("结算费用合计") @NumberFormat("0.00") private BigDecimal settlementAmount;
@ExcelIgnore private Map<String, BigDecimal> feeItems;
@ExcelIgnore private String errorMessage;
@@ -40,6 +40,7 @@
<result column="qualification_back" property="qualificationBack"/>
<result column="driver_type" property="driverType"/>
<result column="mobile" property="mobile"/>
<result column="user_id" property="userId"/>
<result column="contact_relation" property="contactRelation"/>
<result column="organization_name" property="organizationName"/>
<result column="emergency_contact_name" property="emergencyContactName"/>
@@ -85,6 +86,7 @@
qualification_back,
driver_type,
mobile,
user_id,
contact_relation,
organization_name,
emergency_contact_name,
@@ -13,6 +13,7 @@
<result column="status" property="status"/>
<result column="is_deleted" property="isDeleted"/>
<result column="organization_name" property="organizationName"/>
<result column="use_department" property="useDepartment"/>
<result column="plate_no" property="plateNo"/>
<result column="plate_color" property="plateColor"/>
<result column="vehicle_type" property="vehicleType"/>
@@ -60,6 +61,7 @@
status,
is_deleted,
organization_name,
use_department,
plate_no,
plate_color,
vehicle_type,
@@ -116,6 +118,10 @@
<bind name="organizationNameLike" value="'%' + vehicle.organizationName + '%'"/>
AND organization_name LIKE #{organizationNameLike}
</if>
<if test="vehicle.useDepartment != null and vehicle.useDepartment != ''">
<bind name="useDepartmentLike" value="'%' + vehicle.useDepartment + '%'"/>
AND use_department LIKE #{useDepartmentLike}
</if>
<if test="vehicle.plateNo != null and vehicle.plateNo != ''">
<bind name="plateNoLike" value="'%' + vehicle.plateNo + '%'"/>
AND plate_no LIKE #{plateNoLike}
@@ -6,9 +6,20 @@ package org.springblade.transport.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springblade.transport.pojo.entity.WaybillImportBatch;
/** 运单批次 Mapper。 */
@Mapper
public interface WaybillImportBatchMapper extends BaseMapper<WaybillImportBatch> {
/**
* 查询指定租户、指定日期前缀下已使用的最大批次流水号。
* 不过滤逻辑删除数据,避免唯一索引仍占用历史编号时重复生成。
*/
@Select("SELECT COALESCE(MAX(CAST(SUBSTRING(batch_no, CHAR_LENGTH(#{prefix}) + 1) AS UNSIGNED)), 0) "
+ "FROM blade_waybill_import_batch WHERE tenant_id = #{tenantId} "
+ "AND batch_no LIKE CONCAT(#{prefix}, '%')")
Long selectMaxDailySequence(@Param("tenantId") String tenantId, @Param("prefix") String prefix);
}
@@ -25,6 +25,7 @@ import java.util.List;
public interface ITransportReconciliationService extends BaseService<TransportReconciliation> {
IPage<TransportReconciliationVO> selectPage(IPage<TransportReconciliation> page, TransportReconciliationVO query);
IPage<FormalSettlement> formalOptions(IPage<FormalSettlement> page, String settlementType, String keyword);
List<String> templateFeeItems(Long id, Long formalSettlementId, String feeItems);
TransportReconciliationVO detail(Long id);
Long saveDraft(TransportReconciliationSaveRequest request);
void removeDraft(Long id);
@@ -15,4 +15,5 @@ public interface IWaybillImportBatchService extends BaseService<WaybillImportBat
void confirm(WaybillImportBatchRequest request, HttpServletResponse response);
IPage<WaybillImportBatchVO> page(IPage<WaybillImportBatch> page, WaybillImportBatchRequest request);
BusinessRemoveResultVO removeBatches(String ids);
String nextBatchNo();
}
@@ -27,13 +27,25 @@ package org.springblade.transport.service.impl;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.feign.ISysClient;
import org.springblade.system.feign.IUserClient;
import org.springblade.system.pojo.entity.User;
import org.springblade.system.pojo.entity.UserInfo;
import org.springblade.system.pojo.enums.UserSex;
import org.springblade.system.pojo.enums.UserType;
import org.springblade.transport.excel.DriverExcel;
import org.springblade.transport.mapper.DriverMapper;
import org.springblade.transport.mapper.TransportVehicleMapper;
import org.springblade.transport.pojo.entity.Driver;
import org.springblade.transport.pojo.entity.TransportVehicle;
import org.springblade.transport.pojo.vo.DriverExpiryStatVO;
import org.springblade.transport.pojo.vo.DriverVO;
import org.springblade.transport.service.IDriverService;
@@ -50,6 +62,7 @@ import java.util.Objects;
* @author Chill
*/
@Service
@RequiredArgsConstructor
public class DriverServiceImpl extends BaseServiceImpl<DriverMapper, Driver> implements IDriverService {
private static final int NAME_MAX_LENGTH = 10;
@@ -62,6 +75,10 @@ public class DriverServiceImpl extends BaseServiceImpl<DriverMapper, Driver> imp
private static final int DEFAULT_ENABLED_STATUS = 1;
private static final int DEFAULT_FALSE = 0;
private final TransportVehicleMapper transportVehicleMapper;
private final IUserClient userClient;
private final ISysClient sysClient;
@Override
public IPage<DriverVO> selectDriverPage(IPage<DriverVO> page, DriverVO driver) {
prepareQuery(driver);
@@ -74,7 +91,13 @@ public class DriverServiceImpl extends BaseServiceImpl<DriverMapper, Driver> imp
prepare(driver);
validate(driver);
prepareSubmitTarget(driver);
return saveOrUpdate(driver);
// 编辑场景下字段无变化时 update 可能返回 false,不能据此跳过用户同步
saveOrUpdate(driver);
if (Func.isEmpty(driver.getId())) {
throw new ServiceException("保存司机失败");
}
syncDriverUser(driver);
return true;
}
@Override
@@ -228,6 +251,14 @@ public class DriverServiceImpl extends BaseServiceImpl<DriverMapper, Driver> imp
validateLength(driver.getMobile(), MOBILE_MAX_LENGTH, "手机号不能超过20字");
validateLength(driver.getEmergencyContactMobile(), MOBILE_MAX_LENGTH, "紧急联系人手机号不能超过20字");
validateLength(driver.getDrivingVehicle(), VEHICLE_NO_MAX_LENGTH, "驾驶车辆不能超过30字");
if (Func.isNotEmpty(driver.getDrivingVehicle())) {
Long vehicleCount = transportVehicleMapper.selectCount(Wrappers.<TransportVehicle>lambdaQuery()
.eq(TransportVehicle::getIsDeleted, 0)
.eq(TransportVehicle::getPlateNo, driver.getDrivingVehicle()));
if (vehicleCount == null || vehicleCount <= 0) {
throw new ServiceException("驾驶车辆不存在,请重新选择");
}
}
validateLength(driver.getDrivingLicenseNo(), SHORT_TEXT_MAX_LENGTH, "驾驶证档案编号不能超过50字");
validateLength(driver.getQualificationNo(), SHORT_TEXT_MAX_LENGTH, "资格证号不能超过50字");
validateLength(driver.getOrganizationName(), SHORT_TEXT_MAX_LENGTH, "所属组织不能超过50字");
@@ -273,6 +304,164 @@ public class DriverServiceImpl extends BaseServiceImpl<DriverMapper, Driver> imp
}
}
private void syncDriverUser(Driver driver) {
String tenantId = AuthUtil.getTenantId();
if (Func.isEmpty(tenantId)) {
tenantId = driver.getTenantId();
}
if (Func.isEmpty(tenantId)) {
throw new ServiceException("租户信息为空,无法同步系统用户");
}
String mobile = driver.getMobile();
if (Func.isEmpty(mobile) || mobile.length() < 6) {
throw new ServiceException("手机号长度不足,无法同步系统用户");
}
// 编辑时前端可能不传 userId,从库中补齐已关联用户
if (Func.isEmpty(driver.getUserId()) && Func.isNotEmpty(driver.getId())) {
Driver dbDriver = getById(driver.getId());
if (dbDriver != null && Func.isNotEmpty(dbDriver.getUserId())) {
driver.setUserId(dbDriver.getUserId());
}
}
R<String> roleResult = sysClient.getRoleIdByAlias(tenantId, "driver");
if (roleResult == null || !R.isSuccess(roleResult) || Func.isEmpty(roleResult.getData())) {
throw new ServiceException("未配置角色别名为driver的角色,请先在系统角色中维护");
}
String roleId = roleResult.getData();
R<String> deptResult = sysClient.getDeptIds(tenantId, driver.getOrganizationName());
if (deptResult == null || !R.isSuccess(deptResult) || Func.isEmpty(deptResult.getData())) {
throw new ServiceException("所属组织未匹配到系统部门,无法同步用户");
}
String deptId = deptResult.getData();
User existing = findExistingUser(driver, tenantId, mobile);
if (existing == null || Func.isEmpty(existing.getId())) {
// 司机未关联有效用户时新建;密码为手机号后6位
createAndLinkDriverUser(driver, tenantId, mobile, roleId, deptId);
} else {
updateAndLinkDriverUser(driver, existing, tenantId, mobile, roleId, deptId);
}
}
private User findExistingUser(Driver driver, String tenantId, String mobile) {
if (Func.isNotEmpty(driver.getUserId())) {
R<User> byId = userClient.userInfoById(driver.getUserId());
if (byId != null && R.isSuccess(byId) && byId.getData() != null && Func.isNotEmpty(byId.getData().getId())) {
return byId.getData();
}
// 关联的用户已不存在,清空后按手机号重建
driver.setUserId(null);
}
return findExistingUserByAccountOrPhone(tenantId, mobile);
}
private User findExistingUserByAccountOrPhone(String tenantId, String mobile) {
R<User> byAccount = userClient.userByAccount(tenantId, mobile);
if (byAccount != null && R.isSuccess(byAccount) && byAccount.getData() != null
&& Func.isNotEmpty(byAccount.getData().getId())) {
return byAccount.getData();
}
R<UserInfo> byPhone = userClient.userInfoByPhone(tenantId, mobile, UserType.WEB.getName());
if (byPhone != null && R.isSuccess(byPhone) && byPhone.getData() != null
&& byPhone.getData().getUser() != null && Func.isNotEmpty(byPhone.getData().getUser().getId())) {
return byPhone.getData().getUser();
}
return null;
}
private void createAndLinkDriverUser(Driver driver, String tenantId, String mobile, String roleId, String deptId) {
User user = buildSyncUser(null, tenantId, mobile, driver.getDriverName(), driver.getGender(), roleId, deptId);
user.setPassword(mobile.substring(mobile.length() - 6));
user.setUserType(UserType.WEB.getCategory());
R<Boolean> saveResult = null;
try {
saveResult = userClient.saveUser(user);
} catch (Exception ex) {
User existed = findExistingUserByAccountOrPhone(tenantId, mobile);
if (existed != null) {
updateAndLinkDriverUser(driver, existed, tenantId, mobile, roleId, deptId);
return;
}
throw new ServiceException(Func.isNotEmpty(ex.getMessage()) ? ex.getMessage() : "同步系统用户失败");
}
if (!isFeignSuccess(saveResult)) {
User existed = findExistingUserByAccountOrPhone(tenantId, mobile);
if (existed != null) {
updateAndLinkDriverUser(driver, existed, tenantId, mobile, roleId, deptId);
return;
}
throw new ServiceException(resolveFeignError(saveResult, "同步系统用户失败"));
}
R<User> created = userClient.userByAccount(tenantId, mobile);
User createdUser = (created != null && R.isSuccess(created)) ? created.getData() : null;
if (createdUser == null || Func.isEmpty(createdUser.getId())) {
createdUser = findExistingUserByAccountOrPhone(tenantId, mobile);
}
if (createdUser == null || Func.isEmpty(createdUser.getId())) {
throw new ServiceException("系统用户创建成功但无法回查用户ID");
}
linkDriverUserId(driver, createdUser.getId());
}
private void updateAndLinkDriverUser(Driver driver, User existing, String tenantId, String mobile, String roleId, String deptId) {
User user = buildSyncUser(existing.getId(), tenantId, mobile, driver.getDriverName(), driver.getGender(), roleId, deptId);
R<Boolean> updateResult = userClient.updateUser(user);
// updateById 无字段变化时可能返回 false,但业务上仍视为成功,以接口 code 为准
if (!isFeignSuccess(updateResult)) {
throw new ServiceException(resolveFeignError(updateResult, "同步更新系统用户失败"));
}
linkDriverUserId(driver, existing.getId());
}
private boolean isFeignSuccess(R<?> result) {
return result != null && R.isSuccess(result);
}
private String resolveFeignError(R<?> result, String defaultMsg) {
if (result == null || Func.isEmpty(result.getMsg())) {
return defaultMsg;
}
String msg = result.getMsg().trim();
// R.data(false) 也会带默认成功文案,不能当作业务错误抛出
if ("操作成功".equals(msg) || "success".equalsIgnoreCase(msg)) {
return defaultMsg;
}
return msg;
}
private User buildSyncUser(Long userId, String tenantId, String mobile, String driverName, String gender,
String roleId, String deptId) {
User user = new User();
user.setId(userId);
user.setTenantId(tenantId);
user.setAccount(mobile);
user.setPhone(mobile);
user.setName(driverName);
user.setRealName(driverName);
user.setSex(UserSex.getCodeByName(gender));
user.setRoleId(roleId);
user.setDeptId(deptId);
return user;
}
private void linkDriverUserId(Driver driver, Long userId) {
if (Func.isEmpty(userId)) {
return;
}
if (Objects.equals(driver.getUserId(), userId)) {
return;
}
driver.setUserId(userId);
Driver patch = new Driver();
patch.setId(driver.getId());
patch.setUserId(userId);
updateById(patch);
}
private Long defaultZero(Long value) {
return value == null ? 0L : value;
}
@@ -348,6 +348,7 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
projectApply.setTransportRoute(TransportBusinessSupport.trimToNull(projectApply.getTransportRoute()));
projectApply.setTransportType(TransportBusinessSupport.trimToNull(projectApply.getTransportType()));
projectApply.setBusinessType(TransportBusinessSupport.trimToNull(projectApply.getBusinessType()));
projectApply.setBusinessMode(TransportBusinessSupport.trimToNull(projectApply.getBusinessMode()));
normalizeOptionalFields(projectApply);
projectApply.setSettlementMode(TransportBusinessSupport.trimToNull(projectApply.getSettlementMode()));
projectApply.setHandlerUserName(TransportBusinessSupport.trimToNull(projectApply.getHandlerUserName()));
@@ -364,6 +365,9 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
if (projectApply.getEstimatedProfit() != null && projectApply.getEstimatedProfit().signum() < 0) {
projectApply.setEstimatedProfit(null);
}
if (projectApply.getProfitRate() != null && projectApply.getProfitRate().signum() < 0) {
projectApply.setProfitRate(null);
}
if (projectApply.getFundDemand() != null && projectApply.getFundDemand().signum() < 0) {
projectApply.setFundDemand(null);
}
@@ -390,9 +394,11 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
target.setTransportRoute(source.getTransportRoute());
target.setTransportType(source.getTransportType());
target.setBusinessType(source.getBusinessType());
target.setBusinessMode(source.getBusinessMode());
target.setProjectScale(source.getProjectScale());
target.setSettlementMode(source.getSettlementMode());
target.setEstimatedProfit(source.getEstimatedProfit());
target.setProfitRate(source.getProfitRate());
target.setFundDemand(source.getFundDemand());
target.setHandlerUserId(source.getHandlerUserId());
target.setHandlerUserName(source.getHandlerUserName());
@@ -420,6 +426,7 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
TransportBusinessSupport.validateRequired(projectApply.getProjectShortName(), "请输入项目简称");
TransportBusinessSupport.validateRequired(projectApply.getBusinessDeptName(), "请选择业务部门");
TransportBusinessSupport.validateRequired(projectApply.getUndertakeDeptName(), "请选择承办部门");
TransportBusinessSupport.validateRequired(projectApply.getBusinessMode(), "请选择业务模式");
TransportBusinessSupport.validateRequired(projectApply.getCustomerNames(), "请选择客户");
TransportBusinessSupport.validateRequired(projectApply.getCarrierNames(), "请选择下游承运商");
validateAmount(projectApply.getFundLimit(), "项目资金使用额度", false);
@@ -431,6 +438,7 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
validateDays(projectApply.getPaymentDays(), "回款账期");
validateAmount(projectApply.getProjectScale(), "项目规模", true);
validateAmount(projectApply.getEstimatedProfit(), "预计利润", true);
validateAmount(projectApply.getProfitRate(), "利润率", true);
validateAmount(projectApply.getFundDemand(), "资金需求", true);
TransportBusinessSupport.validateDateRange(projectApply.getBusinessStartDate(), projectApply.getBusinessEndDate(), "业务周期开始日期不能晚于结束日期");
if (Func.isEmpty(projectApply.getHandlerUserId()) || Func.isEmpty(projectApply.getPrincipalUserId())) {
@@ -65,6 +65,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -93,6 +94,7 @@ public class TransportReconciliationServiceImpl
private static final String DUPLICATE = "suspected_duplicate";
private static final String MASTER_ORDER_SOURCE = "总单系统生成";
private static final String LOADING_ORDER_SOURCE = "配载单系统生成";
private static final String FORMAL_DRAFT = "draft";
private final FormalSettlementMapper formalSettlementMapper;
private final FormalSettlementSourceMapper formalSourceMapper;
private final FormalSettlementDetailMapper formalDetailMapper;
@@ -134,7 +136,7 @@ public class TransportReconciliationServiceImpl
.select(TransportReconciliation::getFormalSettlementId))
.stream().map(TransportReconciliation::getFormalSettlementId).filter(Objects::nonNull).toList();
LambdaQueryWrapper<FormalSettlement> wrapper = Wrappers.<FormalSettlement>lambdaQuery()
.eq(FormalSettlement::getApprovalStatus, "approved")
.eq(FormalSettlement::getApprovalStatus, FORMAL_DRAFT)
.eq(Func.isNotEmpty(settlementType), FormalSettlement::getSettlementType, settlementType)
.and(Func.isNotEmpty(keyword), value -> value.like(FormalSettlement::getFormalSettlementNo, keyword)
.or().like(FormalSettlement::getContractNo, keyword).or().like(FormalSettlement::getContractName, keyword));
@@ -142,6 +144,60 @@ public class TransportReconciliationServiceImpl
return formalSettlementMapper.selectPage(page, wrapper.orderByDesc(FormalSettlement::getCreateTime));
}
@Override
public List<String> templateFeeItems(Long id, Long formalSettlementId, String feeItems) {
LinkedHashSet<String> names = new LinkedHashSet<>();
appendTemplateFeeItems(names, feeItems);
if (id != null) {
for (TransportReconciliationInternal row : internalRows(id)) {
parseFeeItems(row.getFeeItemsJson()).keySet().forEach(name -> appendTemplateFeeItem(names, name));
}
TransportReconciliation bill = getById(id);
if (bill != null) collectFormalTemplateFeeItems(names, bill.getFormalSettlementId());
}
collectFormalTemplateFeeItems(names, formalSettlementId);
return new ArrayList<>(names);
}
private void appendTemplateFeeItems(Set<String> names, String feeItems) {
if (Func.isEmpty(feeItems)) return;
for (String name : feeItems.split(",")) appendTemplateFeeItem(names, name);
}
private void collectFormalTemplateFeeItems(Set<String> names, Long formalSettlementId) {
if (formalSettlementId == null) return;
List<FormalSettlementSummaryFee> summaries = formalSummaryFeeMapper.selectList(
Wrappers.<FormalSettlementSummaryFee>lambdaQuery()
.eq(FormalSettlementSummaryFee::getFormalSettlementId, formalSettlementId)
.eq(FormalSettlementSummaryFee::getIsDeleted, 0)
.orderByAsc(FormalSettlementSummaryFee::getLineNo));
for (FormalSettlementSummaryFee summary : summaries) {
appendTemplateFeeItem(names, summary.getFeeItem());
}
List<FormalSettlementDetail> details = formalDetailMapper.selectList(
Wrappers.<FormalSettlementDetail>lambdaQuery()
.eq(FormalSettlementDetail::getFormalSettlementId, formalSettlementId)
.orderByAsc(FormalSettlementDetail::getLineNo));
for (FormalSettlementDetail detail : details) {
parseFeeItems(detail.getFeeItemsJson()).keySet().forEach(name -> appendTemplateFeeItem(names, name));
List<FormalSettlementDetailFee> fees = formalDetailFeeMapper.selectList(
Wrappers.<FormalSettlementDetailFee>lambdaQuery()
.eq(FormalSettlementDetailFee::getFormalSettlementDetailId, detail.getId())
.eq(FormalSettlementDetailFee::getIsDeleted, 0)
.orderByAsc(FormalSettlementDetailFee::getLineNo));
for (FormalSettlementDetailFee fee : fees) {
parseFeeItems(fee.getFeeItemsJson()).keySet().forEach(name -> appendTemplateFeeItem(names, name));
}
}
}
private void appendTemplateFeeItem(Set<String> names, String name) {
if (Func.isEmpty(name)) return;
String trimmed = name.trim();
if (trimmed.isEmpty() || isFreightFeeItem(trimmed) || "费用项目1".equals(trimmed)) return;
names.add(trimmed);
}
@Override
public TransportReconciliationVO detail(Long id) {
TransportReconciliationVO vo = TransportReconciliationWrapper.build().entityVO(existing(id));
@@ -166,7 +222,9 @@ public class TransportReconciliationServiceImpl
throw new ServiceException("请选择正确的对账模式");
}
FormalSettlement formal = formalSettlementMapper.selectById(request.getFormalSettlementId());
if (formal == null || !"approved".equals(formal.getApprovalStatus())) throw new ServiceException("请选择已生效的正式结算单");
if (formal == null || !FORMAL_DRAFT.equals(formal.getApprovalStatus())) {
throw new ServiceException("请选择草稿状态的正式结算单");
}
long occupied = count(Wrappers.<TransportReconciliation>lambdaQuery()
.eq(TransportReconciliation::getFormalSettlementId, formal.getId())
.ne(request.getId() != null, TransportReconciliation::getId, request.getId()));
@@ -394,9 +452,9 @@ public class TransportReconciliationServiceImpl
external.setActualCompletionTime(parseTimeNullable(row.getActualCompletionTime(), "实际完成时间"));
Map<String, BigDecimal> feeItems = row.getFeeItems();
if (feeItems == null || feeItems.isEmpty()) {
feeItems = new HashMap<>();
feeItems.put("水费", money(row.getFeeItemOne()));
feeItems.put("罚款", money(row.getFeeItemTwo()));
feeItems = new LinkedHashMap<>();
if (row.getFeeItemOne() != null) feeItems.put("水费", money(row.getFeeItemOne()));
if (row.getFeeItemTwo() != null) feeItems.put("罚款", money(row.getFeeItemTwo()));
}
external.setFeeItemsJson(JsonUtil.toJson(feeItems));
external.setMatchStatus(UNMATCHED);
@@ -434,7 +492,7 @@ public class TransportReconciliationServiceImpl
addImportDecimalErrors(validationErrors, row.getMileage(), "里程(KM", 2);
addImportDecimalErrors(validationErrors, row.getUnitPrice(), "运输单价", 2);
addImportDecimalErrors(validationErrors, row.getFreightAmount(), "运输费", 2);
if (row.getFeeItems() != null) {
if (row.getFeeItems() != null && !row.getFeeItems().isEmpty()) {
row.getFeeItems().forEach((name, value) -> addImportDecimalErrors(validationErrors, value, name, 2));
} else {
addImportDecimalErrors(validationErrors, row.getFeeItemOne(), "水费", 2);
@@ -28,12 +28,15 @@ package org.springblade.transport.service.impl;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
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.transport.excel.TransportVehicleExcel;
import org.springblade.transport.mapper.DriverMapper;
import org.springblade.transport.mapper.TransportVehicleMapper;
import org.springblade.transport.pojo.entity.Driver;
import org.springblade.transport.pojo.entity.TransportVehicle;
import org.springblade.transport.pojo.vo.TransportVehicleExpiryStatVO;
import org.springblade.transport.pojo.vo.TransportVehicleVO;
@@ -42,9 +45,13 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* 车辆管理 服务实现类
@@ -52,9 +59,13 @@ import java.util.regex.Pattern;
* @author Chill
*/
@Service
@RequiredArgsConstructor
public class TransportVehicleServiceImpl extends BaseServiceImpl<TransportVehicleMapper, TransportVehicle> implements ITransportVehicleService {
private static final Pattern PLATE_NO_PATTERN = Pattern.compile("^[\\u4e00-\\u9fa5][A-Z][A-Z0-9挂学警港澳]{5,6}$");
private static final String PLATE_COLOR_BLUE = "蓝牌";
private static final String PLATE_COLOR_YELLOW = "黄牌";
private static final String PLATE_COLOR_GREEN = "绿牌";
private static final int SHORT_TEXT_MAX_LENGTH = 50;
private static final int ORG_MAX_LENGTH = 50;
private static final int REMARK_MAX_LENGTH = 200;
@@ -64,10 +75,55 @@ public class TransportVehicleServiceImpl extends BaseServiceImpl<TransportVehicl
private static final int CERTIFICATION_APPROVED = 1;
private static final int CERTIFICATION_REJECTED = 2;
private final DriverMapper driverMapper;
@Override
public IPage<TransportVehicleVO> selectTransportVehiclePage(IPage<TransportVehicleVO> page, TransportVehicleVO vehicle) {
prepareQuery(vehicle);
return page.setRecords(baseMapper.selectTransportVehiclePage(page, vehicle));
List<TransportVehicleVO> records = baseMapper.selectTransportVehiclePage(page, vehicle);
fillBoundDrivers(records);
return page.setRecords(records);
}
/**
* 按车牌反查司机驾驶车辆,填充车辆列表「绑定司机」。
*/
private void fillBoundDrivers(List<TransportVehicleVO> records) {
if (Func.isEmpty(records)) {
return;
}
List<String> plateNos = records.stream()
.map(TransportVehicleVO::getPlateNo)
.filter(Func::isNotEmpty)
.map(String::toUpperCase)
.distinct()
.toList();
if (plateNos.isEmpty()) {
return;
}
List<Driver> drivers = driverMapper.selectList(Wrappers.<Driver>lambdaQuery()
.eq(Driver::getIsDeleted, 0)
.in(Driver::getDrivingVehicle, plateNos)
.select(Driver::getDriverName, Driver::getDrivingVehicle));
Map<String, List<String>> namesByPlate = new LinkedHashMap<>();
for (Driver driver : drivers) {
if (Func.isEmpty(driver.getDrivingVehicle()) || Func.isEmpty(driver.getDriverName())) {
continue;
}
namesByPlate
.computeIfAbsent(driver.getDrivingVehicle().toUpperCase(), key -> new ArrayList<>())
.add(driver.getDriverName());
}
for (TransportVehicleVO record : records) {
if (Func.isEmpty(record.getPlateNo())) {
continue;
}
List<String> names = namesByPlate.get(record.getPlateNo().toUpperCase());
if (Func.isEmpty(names)) {
continue;
}
record.setBoundDriver(names.stream().distinct().collect(Collectors.joining("")));
}
}
@Override
@@ -148,8 +204,10 @@ public class TransportVehicleServiceImpl extends BaseServiceImpl<TransportVehicl
private void prepare(TransportVehicle vehicle) {
vehicle.setOrganizationName(trimToEmpty(vehicle.getOrganizationName()));
vehicle.setUseDepartment(trimToNull(vehicle.getUseDepartment()));
vehicle.setPlateNo(trimToEmpty(vehicle.getPlateNo()).toUpperCase());
vehicle.setPlateColor(trimToNull(vehicle.getPlateColor()));
// 车牌颜色由车牌号规则推断,不接收前端传入值
vehicle.setPlateColor(resolvePlateColor(vehicle.getPlateNo()));
vehicle.setVehicleType(trimToEmpty(vehicle.getVehicleType()));
vehicle.setBusinessRelation(trimToEmpty(vehicle.getBusinessRelation()));
vehicle.setEnergyType(trimToEmpty(vehicle.getEnergyType()));
@@ -181,6 +239,22 @@ public class TransportVehicleServiceImpl extends BaseServiceImpl<TransportVehicl
}
}
/**
* 根据车牌号推断车牌颜色:新能源 8 位为绿牌,含“挂”为黄牌,其余为蓝牌。
*/
private String resolvePlateColor(String plateNo) {
if (Func.isEmpty(plateNo)) {
return null;
}
if (plateNo.length() == 8) {
return PLATE_COLOR_GREEN;
}
if (plateNo.contains("")) {
return PLATE_COLOR_YELLOW;
}
return PLATE_COLOR_BLUE;
}
private void validate(TransportVehicle vehicle) {
if (Func.isEmpty(vehicle.getOrganizationName())) {
throw new ServiceException("所属组织不能为空");
@@ -192,7 +266,7 @@ public class TransportVehicleServiceImpl extends BaseServiceImpl<TransportVehicl
throw new ServiceException("车牌号格式不正确");
}
if (Func.isEmpty(vehicle.getPlateColor())) {
throw new ServiceException("车牌颜色不能为空");
throw new ServiceException("无法根据车牌号识别车牌颜色");
}
if (Func.isEmpty(vehicle.getVehicleType())) {
throw new ServiceException("车辆类型不能为空");
@@ -230,7 +304,7 @@ public class TransportVehicleServiceImpl extends BaseServiceImpl<TransportVehicl
validateNonNegative(vehicle.getApprovedLoadKg(), "核定载质量不能小于0");
validateNonNegative(vehicle.getTractionMassKg(), "准牵引总质量不能小于0");
validateLength(vehicle.getOrganizationName(), ORG_MAX_LENGTH, "所属组织不能超过50字");
validateLength(vehicle.getPlateColor(), SHORT_TEXT_MAX_LENGTH, "车牌颜色不能超过50字");
validateLength(vehicle.getUseDepartment(), ORG_MAX_LENGTH, "使用部门不能超过50字");
validateLength(vehicle.getVehicleType(), SHORT_TEXT_MAX_LENGTH, "车辆类型不能超过50字");
validateLength(vehicle.getBusinessRelation(), SHORT_TEXT_MAX_LENGTH, "业务关系不能超过50字");
validateLength(vehicle.getEnergyType(), SHORT_TEXT_MAX_LENGTH, "能源类型不能超过50字");
@@ -13,6 +13,7 @@ import lombok.RequiredArgsConstructor;
import org.springblade.common.excel.ImportFailureExcelUtil;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.DateUtil;
@@ -258,7 +259,7 @@ public class WaybillImportBatchServiceImpl extends BaseServiceImpl<WaybillImport
if (Func.isEmpty(request.getImportType())) throw new ServiceException("请选择导入类型");
WaybillImportBatch batch = new WaybillImportBatch();
BeanUtil.copyProperties(request, batch);
batch.setBatchNo(Func.isEmpty(request.getBatchNo()) ? nextCode() : request.getBatchNo());
batch.setBatchNo(Func.isEmpty(request.getId()) ? nextCode() : request.getBatchNo());
batch.setCarrierIds(joinIds(request.getCarrierIds()));
batch.setCarrierName(resolveCarrierNames(request.getCarrierIds(), request.getCarrierName()));
batch.setImportStatus(importStatus);
@@ -422,10 +423,15 @@ public class WaybillImportBatchServiceImpl extends BaseServiceImpl<WaybillImport
return vo;
}
@Override
public String nextBatchNo() {
return nextCode();
}
private synchronized String nextCode() {
String prefix = "YDB" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
long count = count(Wrappers.<WaybillImportBatch>lambdaQuery().likeRight(WaybillImportBatch::getBatchNo, prefix));
return prefix + String.format("%04d", count + 1);
String prefix = "PC" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
Long maxSequence = baseMapper.selectMaxDailySequence(AuthUtil.getTenantId(), prefix);
return prefix + String.format("%04d", (maxSequence == null ? 0 : maxSequence) + 1);
}
/**
@@ -0,0 +1,4 @@
-- 项目立项增加业务模式、利润率
ALTER TABLE `blade_project_apply`
ADD COLUMN `business_mode` varchar(50) DEFAULT NULL COMMENT '业务模式' AFTER `business_type`,
ADD COLUMN `profit_rate` decimal(18,2) DEFAULT NULL COMMENT '利润率(%)' AFTER `estimated_profit`;
@@ -33,8 +33,10 @@ CREATE TABLE `blade_project_apply` (
`transport_route` varchar(200) DEFAULT NULL COMMENT '运输线路',
`transport_type` varchar(100) DEFAULT NULL COMMENT '运输类型',
`business_type` varchar(100) DEFAULT NULL COMMENT '业务类型',
`business_mode` varchar(50) DEFAULT NULL COMMENT '业务模式',
`project_scale` decimal(18,2) DEFAULT NULL COMMENT '项目规模(万元)',
`estimated_profit` decimal(18,2) DEFAULT NULL COMMENT '预计利润(万元)',
`profit_rate` decimal(18,2) DEFAULT NULL COMMENT '利润率(%)',
`fund_demand` decimal(18,2) DEFAULT NULL COMMENT '资金需求(万元)',
`settlement_mode` varchar(100) DEFAULT NULL COMMENT '结算方式',
`handler_user_id` bigint(20) DEFAULT NULL COMMENT '项目经办人ID',
@@ -33,6 +33,7 @@ CREATE TABLE `blade_transport_driver` (
`qualification_back` varchar(1000) DEFAULT NULL COMMENT '从业资格证内容页',
`driver_type` varchar(20) NOT NULL COMMENT '司机类型:自有/外协',
`mobile` varchar(20) NOT NULL COMMENT '手机号',
`user_id` bigint(20) DEFAULT NULL COMMENT '关联系统用户ID',
`contact_relation` varchar(20) DEFAULT NULL COMMENT '与联系人关系',
`organization_name` varchar(50) NOT NULL COMMENT '所属组织',
`emergency_contact_name` varchar(20) NOT NULL COMMENT '紧急联系人姓名',
@@ -0,0 +1,6 @@
-- 司机同步系统用户依赖角色别名 role_alias = 'driver'
-- 请在系统管理 -> 角色管理中为对应租户新增/维护角色,并设置角色别名为 driver
-- 参考 blade_role 表结构:id, tenant_id, parent_id, role_name, sort, role_alias, status, is_deleted
-- 示例(请按实际租户与主键策略自行调整,勿直接照抄执行):
-- INSERT INTO blade_role (id, tenant_id, parent_id, role_name, sort, role_alias, status, is_deleted)
-- VALUES (你的雪花ID, '你的租户ID', 0, '司机', 10, 'driver', 1, 0);
@@ -0,0 +1,3 @@
-- 司机表增加关联系统用户ID
ALTER TABLE `blade_transport_driver`
ADD COLUMN `user_id` bigint(20) DEFAULT NULL COMMENT '关联系统用户ID' AFTER `mobile`;
@@ -6,6 +6,7 @@ CREATE TABLE `blade_transport_vehicle` (
`id` bigint(20) NOT NULL COMMENT '主键',
`tenant_id` varchar(12) DEFAULT '000000' COMMENT '租户ID',
`organization_name` varchar(50) NOT NULL COMMENT '所属组织',
`use_department` varchar(50) DEFAULT NULL COMMENT '使用部门',
`plate_no` varchar(16) NOT NULL COMMENT '车牌号',
`plate_color` varchar(20) DEFAULT NULL COMMENT '车牌颜色',
`vehicle_type` varchar(50) NOT NULL COMMENT '车辆类型',
@@ -0,0 +1,3 @@
-- 车辆表增加使用部门
ALTER TABLE `blade_transport_vehicle`
ADD COLUMN `use_department` varchar(50) DEFAULT NULL COMMENT '使用部门' AFTER `organization_name`;