✨ 小程序档案管理支持组织树选择与大头照清空

- 新增 GET /archive/manage/dept/tree,直读本租户启用中的部门并组装成树,剔除「外部组织」整支
- 司机保存时大头照为空则显式置 null,否则 update-strategy=not_null 会保留旧值
This commit is contained in:
2026-10-28 18:20:35 +08:00
parent 85a3416b6e
commit afe651f612
6 changed files with 174 additions and 1 deletions
@@ -29,12 +29,15 @@ import lombok.AllArgsConstructor;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.tool.api.R;
import org.springblade.transport.pojo.vo.ArchiveManageStatsVO;
import org.springblade.transport.pojo.vo.DeptTreeNodeVO;
import org.springblade.transport.service.IManageArchiveService;
import org.springblade.transport.support.MpUserDataScopeSupport;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 调度端档案(小程序管理端)
* <p>
@@ -53,6 +56,13 @@ import org.springframework.web.bind.annotation.RestController;
@Tag(name = "调度端档案", description = "小程序调度端资料页档案统计")
public class ManageArchiveController extends BladeController {
/**
* 权限不足的业务码。小程序请求层把 401 当会话失效,接口一律不得回 401。
*/
private static final int FORBIDDEN_CODE = 403;
private static final String FORBIDDEN_MSG = "仅调度端管理员可选择组织";
private final IManageArchiveService manageArchiveService;
private final MpUserDataScopeSupport mpUserDataScopeSupport;
@@ -71,4 +81,14 @@ public class ManageArchiveController extends BladeController {
return R.data(mpUserDataScopeSupport.isMpDispatcher());
}
@GetMapping("/dept/tree")
@ApiOperationSupport(order = 3)
@Operation(summary = "组织树", description = "小程序组织选择器用:启用中的部门树,已剔除「外部组织」整支;非管理员回业务 403")
public R<List<DeptTreeNodeVO>> deptTree() {
if (!mpUserDataScopeSupport.isMpDispatcher()) {
return R.fail(FORBIDDEN_CODE, FORBIDDEN_MSG);
}
return R.data(manageArchiveService.deptTree());
}
}
@@ -23,6 +23,7 @@
package org.springblade.transport.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -33,6 +34,7 @@ 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.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.transport.pojo.entity.Driver;
import org.springblade.transport.pojo.vo.DriverVO;
import org.springblade.transport.service.IDriverService;
@@ -122,7 +124,14 @@ public class ManageArchiveDriverController extends BladeController {
}
// 生效指针只由认证审核流水写(通过才建立生效快照),不接受管理端写入
driver.setEffectiveCertRecordId(null);
return R.status(driverService.submit(driver));
boolean saved = driverService.submit(driver);
// submit 内部把空串 trimToNull,update-strategy=not_null 会跳过该字段,导致删不掉大头照;这里显式清空
if (saved && driver.getId() != null && Func.isBlank(driver.getHeadPhoto())) {
driverService.update(Wrappers.<Driver>lambdaUpdate()
.eq(Driver::getId, driver.getId())
.set(Driver::getHeadPhoto, null));
}
return R.status(saved);
}
/**
@@ -0,0 +1,26 @@
package org.springblade.transport.mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springblade.system.pojo.entity.Dept;
import java.util.List;
/**
* 组织树只读查询(小程序组织选择器)
* <p>
* 不复用 blade-system 的部门接口:那些接口全挂在 {@code @PreAuth(menu = "dept")} 下,
* 小程序令牌的 roleId 是占位值 "0"、查不到 blade_role_menu,任何小程序账号都会被拒。
* 这里直接按租户读表,只取组织选择需要的四列。
*/
public interface ManageArchiveDeptMapper {
/**
* 租户内启用中的部门,按排序号升序(不含已删除)
*/
@Select("SELECT id, parent_id AS parentId, dept_name AS deptName, sort FROM blade_dept "
+ "WHERE is_deleted = 0 AND status = 1 AND tenant_id = #{tenantId} "
+ "ORDER BY sort, id")
List<Dept> selectEnabledByTenant(@Param("tenantId") String tenantId);
}
@@ -0,0 +1,62 @@
/**
* 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>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.transport.pojo.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* 组织树节点(小程序组织选择器)
*
* @author Chill
*/
@Data
@Schema(description = "组织树节点")
public class DeptTreeNodeVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 部门主键:雪花 ID 超出 JS 安全整数,序列化成字符串,前端不做 Number()
*/
@JsonSerialize(using = ToStringSerializer.class)
@Schema(description = "部门主键")
private Long id;
@Schema(description = "部门名称")
private String name;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@Schema(description = "子部门")
private List<DeptTreeNodeVO> children = new ArrayList<>();
}
@@ -23,6 +23,9 @@
package org.springblade.transport.service;
import org.springblade.transport.pojo.vo.ArchiveManageStatsVO;
import org.springblade.transport.pojo.vo.DeptTreeNodeVO;
import java.util.List;
/**
* 调度端档案统计
@@ -34,4 +37,9 @@ public interface IManageArchiveService {
*/
ArchiveManageStatsVO stats();
/**
* 组织树:启用中的部门,已剔除「外部组织」整支
*/
List<DeptTreeNodeVO> deptTree();
}
@@ -23,7 +23,12 @@
package org.springblade.transport.service.impl;
import lombok.AllArgsConstructor;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.pojo.entity.Dept;
import org.springblade.transport.mapper.ManageArchiveDeptMapper;
import org.springblade.transport.pojo.vo.ArchiveManageStatsVO;
import org.springblade.transport.pojo.vo.DeptTreeNodeVO;
import org.springblade.transport.pojo.vo.DriverVO;
import org.springblade.transport.pojo.vo.TransportVehicleVO;
import org.springblade.transport.service.IDriverService;
@@ -31,6 +36,11 @@ import org.springblade.transport.service.IManageArchiveService;
import org.springblade.transport.service.ITransportVehicleService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 调度端档案统计
*/
@@ -38,8 +48,14 @@ import org.springframework.stereotype.Service;
@AllArgsConstructor
public class ManageArchiveServiceImpl implements IManageArchiveService {
/**
* 组织选择器不提供这一支:web 端司机/车辆表单同样剔除「外部组织」。
*/
private static final String EXTERNAL_ORG_NAME = "外部组织";
private final IDriverService driverService;
private final ITransportVehicleService transportVehicleService;
private final ManageArchiveDeptMapper manageArchiveDeptMapper;
@Override
public ArchiveManageStatsVO stats() {
@@ -51,4 +67,36 @@ public class ManageArchiveServiceImpl implements IManageArchiveService {
return vo;
}
@Override
public List<DeptTreeNodeVO> deptTree() {
List<Dept> rows = manageArchiveDeptMapper.selectEnabledByTenant(AuthUtil.getTenantId());
Map<Long, DeptTreeNodeVO> nodes = new LinkedHashMap<>();
for (Dept row : rows) {
DeptTreeNodeVO node = new DeptTreeNodeVO();
node.setId(row.getId());
node.setName(Func.toStr(row.getDeptName()));
nodes.put(row.getId(), node);
}
List<DeptTreeNodeVO> roots = new ArrayList<>();
for (Dept row : rows) {
DeptTreeNodeVO node = nodes.get(row.getId());
DeptTreeNodeVO parent = row.getParentId() == null ? null : nodes.get(row.getParentId());
// 父部门停用或删除时该节点作为根保留,避免整支在树上消失
if (parent == null) {
roots.add(node);
} else {
parent.getChildren().add(node);
}
}
removeExternalOrg(roots);
return roots;
}
private static void removeExternalOrg(List<DeptTreeNodeVO> nodes) {
nodes.removeIf(node -> EXTERNAL_ORG_NAME.equals(Func.toStr(node.getName()).trim()));
for (DeptTreeNodeVO node : nodes) {
removeExternalOrg(node.getChildren());
}
}
}