feat(mcp): 新增出纸机查询工具 hardware.list/detail.query (REQ-20260910-0012)
This commit is contained in:
@@ -279,6 +279,62 @@
|
||||
|
||||
---
|
||||
|
||||
## 10. hardware.list.query
|
||||
|
||||
分页查询出纸机列表,对应老系统 `POST /hardware/listByPage`(HardwareViewController)。
|
||||
|
||||
- 分类:hardware | 类型:读
|
||||
- 数据来源:m0 库 `hardware_view` 视图,固定 `soft_delete = 0`、`id DESC`
|
||||
- 行级权限(`PermissionService.applyHardwareRowFilter`,与站点同一 `getByIsAgent(propertyFlag=true, className="property_id")` 变体):
|
||||
- 总部(isagent=0):全部;
|
||||
- 代理商(isagent=1):`agent_id IN (自己 + 直属下级 + warehouse_agent_auth 代管代理)`,有代管物业授权时再 `OR property_id IN (agent_manage_district_auth 代管物业)`;
|
||||
- 销售部门非负责人:`property_id IN (自己创建的物业 + 代管代理名下物业)`,与上一条互斥。
|
||||
- 富化(修正老接口 N+1,按页批量):`agentName`/`propertyName`/`districtName` 走 Redis key `name:Agent:{id}` / `name:Properyt:{id}` / `name:District:{id}` multiGet;断纸状态 `piStatus` 批量读 `Assignment:{snCode}:29`(无 key 返回 0);本页非空 `imsi` 一次性 IN 查 `networkcard` 表。返回 `hardware_view` 全字段,不做字段级权限裁剪。
|
||||
|
||||
**入参**:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| snCode | string | 否 | sn码,模糊匹配 |
|
||||
| deviceType | number | 否 | 设备类型:0=厕纸机 1=擦手纸机 |
|
||||
| status | number | 否 | 设备状态:1=在线 2=故障 3=离线 |
|
||||
| online | number | 否 | 上下线状态:1=上线 2=下线 |
|
||||
| lockMachineState | number | 否 | 锁机状态:0=未锁机 1=已锁机 |
|
||||
| agentId | number | 否 | 代理商 id,正整数,精确匹配 |
|
||||
| propertyId | number | 否 | 物业 id,正整数,精确匹配 |
|
||||
| districtId | number | 否 | 站点 id,正整数,精确匹配 |
|
||||
| buildingId | number | 否 | 楼 id,正整数,精确匹配 |
|
||||
| floor | string | 否 | 楼层,精确匹配 |
|
||||
| imsi | string | 否 | imsi码,精确匹配 |
|
||||
| page | number | 否 | 页码,从 1 开始,默认 1 |
|
||||
| rows | number | 否 | 每页条数,默认 20,上限 100(超出截断为 100 并在 notice 提示) |
|
||||
|
||||
**出参 records 字段**:`hardware_view` 全字段(snCode、deviceType、status/online/lockMachineState 状态四元组、sysversion/firmwareVersion/voiceVersion/advertiseVersion、imsi、电量/纸量/电池族、各功能开关、memo 等,时间格式 `yyyy-MM-dd HH:mm:ss`)+ 富化字段 `agentName`/`propertyName`/`districtName`/`piStatus` + 物联网卡字段 `factoryType`/`carrier`/`dataBalance`/`dataUsage`/`iccid`/`msisdn`(按 imsi 匹配,无卡为 null)。
|
||||
|
||||
---
|
||||
|
||||
## 11. hardware.detail.query
|
||||
|
||||
按 snCode 或 id 查询出纸机详情(二选一必填),行级权限与 `hardware.list.query` 一致。
|
||||
|
||||
- 分类:hardware | 类型:读
|
||||
- 数据来源:m0 库 `hardware_view` + `district_district_strategy`(站点信息)+ `networkcard`(物联网卡)
|
||||
- 设备不存在或无权查看时 `record=null` 并在 notice 说明(两者不区分,不泄露设备是否存在)。
|
||||
|
||||
**入参**:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| snCode | string | 条件必填 | sn码,精确匹配;与 id 二选一 |
|
||||
| id | number | 条件必填 | 设备 id,正整数,精确匹配;与 snCode 二选一 |
|
||||
|
||||
**出参**:
|
||||
|
||||
- `record`:hardware_view 全字段 + 与列表同一套富化(agentName/propertyName/districtName、piStatus、物联网卡 factoryType/carrier/dataBalance/dataUsage/iccid/msisdn)。
|
||||
- `record.site`(关联站点,district 表现有字段):`districtId`、`districtName`、`province`、`city`、`countyseat`、`address`、`contact`、`phone`、`buildingId`、`floor`。注意 district 表无经纬度和楼名字段,楼座/楼层取 hardware_view。
|
||||
|
||||
---
|
||||
|
||||
## 附录:在线查看工具清单
|
||||
|
||||
```bash
|
||||
|
||||
@@ -21,6 +21,17 @@ SELECT s.id, t.id, 0 FROM agent_scope s JOIN tool t
|
||||
WHERE s.code = 'operation' AND t.name IN ('sys.ping', 'agent.count', 'agent.list.query', 'property.list.query', 'agent.detail.query', 'property.detail.query', 'mall.order.list.query', 'mall.order.detail.query', 'mall.order.logistics.query')
|
||||
AND NOT EXISTS (SELECT 1 FROM scope_tool st WHERE st.scope_id = s.id AND st.tool_id = t.id);
|
||||
|
||||
-- 作用域:hardware(出纸机查询)
|
||||
INSERT INTO agent_scope (name, code, memo, status, create_id)
|
||||
SELECT '出纸机', 'hardware', 'dev 种子:出纸机查询作用域', 1, 0
|
||||
WHERE NOT EXISTS (SELECT 1 FROM agent_scope WHERE code = 'hardware');
|
||||
|
||||
-- 作用域-工具:hardware.list.query / hardware.detail.query
|
||||
INSERT INTO scope_tool (scope_id, tool_id, create_id)
|
||||
SELECT s.id, t.id, 0 FROM agent_scope s JOIN tool t
|
||||
WHERE s.code = 'hardware' AND t.name IN ('hardware.list.query', 'hardware.detail.query')
|
||||
AND NOT EXISTS (SELECT 1 FROM scope_tool st WHERE st.scope_id = s.id AND st.tool_id = t.id);
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- 校验
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
package me.iiv.agent.entity.m0;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* hardware_view 视图(m0 库),字段 1:1 照搬老项目
|
||||
* xiaoqu-publicbeanandmap beans/b0/HardwareView.java。
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@TableName("hardware_view")
|
||||
public class HardwareView implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 创建id */
|
||||
private Long createId;
|
||||
|
||||
/** 创建时间 */
|
||||
private Date createTime;
|
||||
|
||||
/** 修改id */
|
||||
private Long modifyId;
|
||||
|
||||
/** 修改时间 */
|
||||
private Date modifyTime;
|
||||
|
||||
/** 数据状态 */
|
||||
private Integer softDelete;
|
||||
|
||||
/** 设备编号 */
|
||||
private String no;
|
||||
|
||||
/** 位置信息 */
|
||||
private String address;
|
||||
|
||||
/** 位置id */
|
||||
private Long positionId;
|
||||
|
||||
/** 位置信息 */
|
||||
private String position;
|
||||
|
||||
/** sim卡号 */
|
||||
private String simNo;
|
||||
|
||||
/** sn码 */
|
||||
private String snCode;
|
||||
|
||||
/** 状态1:在线;2:故障;3:离线 */
|
||||
private Integer status;
|
||||
|
||||
/** 设备随机码 */
|
||||
private String checkCode;
|
||||
|
||||
/** 固件版本 */
|
||||
private String firmwareVersion;
|
||||
|
||||
/** 厕所id */
|
||||
private Long toiletId;
|
||||
|
||||
/** 楼id */
|
||||
private Long buildingId;
|
||||
|
||||
/** 楼层 */
|
||||
private String floor;
|
||||
|
||||
/** 区域id */
|
||||
private Long districtId;
|
||||
|
||||
/** 物业id */
|
||||
private Long propertyId;
|
||||
|
||||
/** 代理id */
|
||||
private Long agentId;
|
||||
|
||||
/** 设备型号 */
|
||||
private String version;
|
||||
|
||||
/** 备注 */
|
||||
private String memo;
|
||||
|
||||
/** 保洁id */
|
||||
private Long userId;
|
||||
|
||||
/** 电力支持 */
|
||||
private Integer powerSupply;
|
||||
|
||||
/** 感应抽纸长度 */
|
||||
private Integer senseLength;
|
||||
|
||||
/** 扫码抽纸长度 */
|
||||
private Integer scanLength;
|
||||
|
||||
/** 出纸模式 */
|
||||
private Integer paperType;
|
||||
|
||||
/** 纸规格 */
|
||||
private String paperNorms;
|
||||
|
||||
/** 感应出纸次数 */
|
||||
private Integer senseCount;
|
||||
|
||||
/** 扫码出纸次数 */
|
||||
private Integer scanCount;
|
||||
|
||||
/** 剩余次数 */
|
||||
private Integer paperSurplus;
|
||||
|
||||
/** 出纸计数 */
|
||||
private Long paperCount;
|
||||
|
||||
/** sn码 */
|
||||
private String temSnCode;
|
||||
|
||||
/** rfid */
|
||||
private String rfid;
|
||||
|
||||
/** 硬件名称 */
|
||||
private String name;
|
||||
|
||||
/** 安装场景0:厕位内:1:厕位外:2其它 */
|
||||
private Integer installScenes;
|
||||
|
||||
/** 安装方式0:免打孔 1:打孔 */
|
||||
private Integer installMode;
|
||||
|
||||
/** 供电方式 0:电源 1:电池 */
|
||||
private Integer poweredType;
|
||||
|
||||
/** 设备上下线状态 */
|
||||
private Integer online;
|
||||
|
||||
/** 蓝牙名称 */
|
||||
private String bluetoothName;
|
||||
|
||||
/** imsi码 */
|
||||
private String imsi;
|
||||
|
||||
/** 蓝牙地址 */
|
||||
private String bluetoothMac;
|
||||
|
||||
/** 上线时间 */
|
||||
private Date onlineTime;
|
||||
|
||||
/** 芯片号 */
|
||||
private String chip;
|
||||
|
||||
/** 主板号 */
|
||||
private String motherboard;
|
||||
|
||||
private String sysversion;
|
||||
|
||||
/** 感应出纸次数 */
|
||||
private Integer paperState;
|
||||
|
||||
/** 扫码出纸次数 */
|
||||
private Integer powerState;
|
||||
|
||||
private Integer positionState;
|
||||
|
||||
/** 剩余次数 */
|
||||
private Integer useState;
|
||||
|
||||
/** 质保期 */
|
||||
private Integer warrantyPeriod;
|
||||
|
||||
/** 运行模式 */
|
||||
private Integer runlMode;
|
||||
|
||||
/** 锁状态 */
|
||||
private Integer lockState;
|
||||
|
||||
/** 设备类型 */
|
||||
private Integer deviceType;
|
||||
|
||||
/** 清理用纸卷计数 */
|
||||
private Integer cleanRfidCount;
|
||||
|
||||
/** 工程模式0:关;1:开 */
|
||||
private Integer projectModel;
|
||||
|
||||
/** 出纸模式0:关闭;1:开启 */
|
||||
private Integer paperOutState;
|
||||
|
||||
/** 机芯更换时间 */
|
||||
private Date machineChipTime;
|
||||
|
||||
/** 机芯编号 */
|
||||
private String machineChipCode;
|
||||
|
||||
/** 当前机芯编号 */
|
||||
private String currentChip;
|
||||
|
||||
/** 锁定机器状态0:未锁定;1:已锁定 */
|
||||
private Integer lockMachineState;
|
||||
|
||||
/** 语音版本 */
|
||||
private String voiceVersion;
|
||||
|
||||
/** 校准标识0:否;1:是 */
|
||||
private Integer calibratFlag;
|
||||
|
||||
/** 满卷标识0:否;1:是 */
|
||||
private Integer fullRollFlag;
|
||||
|
||||
/** 中校准锁定开关 */
|
||||
private Integer middleFlag;
|
||||
|
||||
/** 低校准锁定开关 */
|
||||
private Integer lowerFlag;
|
||||
|
||||
/** 纸校准锁定开关 */
|
||||
private Integer paperLockFlag;
|
||||
|
||||
/** 低故障开关(0:故障;1:正常) */
|
||||
private Integer lowerFaultFlag;
|
||||
|
||||
/** 中故障开关(0:故障;1:正常) */
|
||||
private Integer middleFaultFlag;
|
||||
|
||||
/** 当前锁定状态出纸次数 */
|
||||
private Integer paperLockCount;
|
||||
|
||||
/** 保修截止日期 */
|
||||
private Date warrantyExpirationTime;
|
||||
|
||||
/** 首次上线时间 */
|
||||
private Date firstOnTime;
|
||||
|
||||
/** 广告机版本 */
|
||||
private String advertiseVersion;
|
||||
|
||||
/** 非法纸禁出状态(0:禁出;1:未禁出) */
|
||||
private Integer illegalPaperDisableState;
|
||||
|
||||
/** 是否绑定NFC 0:否;1:是 */
|
||||
private Integer nfcFlag;
|
||||
|
||||
/** nfcId */
|
||||
private String nfcId;
|
||||
|
||||
/** 定时语音播放0:关;1:开 */
|
||||
private Integer timerVoiceFlag;
|
||||
|
||||
/** 设定时长(分) */
|
||||
private Integer timerVoiceTiming;
|
||||
|
||||
/** 定时出纸0:关;1:开 */
|
||||
private Integer timerPaperoutFlag;
|
||||
|
||||
/** 设定时长(分) */
|
||||
private Integer timerPaperoutTiming;
|
||||
|
||||
/** 定时出纸长度 */
|
||||
private Integer timerPaperoutLength;
|
||||
|
||||
/** 电池电量异常标记0:否;1:是 */
|
||||
private Integer batteryFaultFlag;
|
||||
|
||||
/** 电池温度 */
|
||||
private Integer batteryTemp;
|
||||
|
||||
/** 网络信号 */
|
||||
private Integer networkSignal;
|
||||
|
||||
/** 充电循环次数 */
|
||||
private Integer batteryCircle;
|
||||
|
||||
/** 生产备注 */
|
||||
private String prdMemo;
|
||||
|
||||
/** 备注 */
|
||||
private String runMemo;
|
||||
|
||||
/** 电流 */
|
||||
private Integer current;
|
||||
|
||||
/** 电压 */
|
||||
private Integer voltage;
|
||||
|
||||
/** 计租标记(0否1是) */
|
||||
private Integer rentFlag;
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package me.iiv.agent.entity.m0;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* networkcard 联网卡列表(m0 库),字段照搬老项目
|
||||
* xiaoqu-publicbeanandmap beans/b0/Networkcard.java。
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@TableName("networkcard")
|
||||
public class Networkcard implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 创建id */
|
||||
private Long createId;
|
||||
|
||||
/** 创建时间 */
|
||||
private Date createTime;
|
||||
|
||||
/** 修改id */
|
||||
private Long modifyId;
|
||||
|
||||
/** 修改时间 */
|
||||
private Date modifyTime;
|
||||
|
||||
/** 数据状态 */
|
||||
private Integer softDelete;
|
||||
|
||||
/** sim卡号 */
|
||||
private String simNo;
|
||||
|
||||
/** 设备编号 */
|
||||
private String sncode;
|
||||
|
||||
/** 绑定状态 */
|
||||
private Integer state;
|
||||
|
||||
/** msisdn */
|
||||
private String msisdn;
|
||||
|
||||
/** iccid */
|
||||
private String iccid;
|
||||
|
||||
/** imsi码 */
|
||||
private String imsi;
|
||||
|
||||
/** 本月流量 */
|
||||
private BigDecimal dataUsage;
|
||||
|
||||
/** 运营商 */
|
||||
private String carrier;
|
||||
|
||||
/** 套餐大小 */
|
||||
private Integer dataPlan;
|
||||
|
||||
/** 卡状态 */
|
||||
private Integer accountStatus;
|
||||
|
||||
/** 计费结束日期 */
|
||||
private Integer expiryDate;
|
||||
|
||||
/** 激活状态 0,1 */
|
||||
private Integer active;
|
||||
|
||||
/** 激活日期 */
|
||||
private Integer activeDate;
|
||||
|
||||
/** 剩余流量,单位M */
|
||||
private BigDecimal dataBalance;
|
||||
|
||||
/** 出库日期 */
|
||||
private Integer outboundDate;
|
||||
|
||||
/** 区域/站id */
|
||||
private Integer districtId;
|
||||
|
||||
/** 所属物业 */
|
||||
private Long propertyId;
|
||||
|
||||
/** 代理商id */
|
||||
private Long agentId;
|
||||
|
||||
/** 楼id */
|
||||
private Long buildingId;
|
||||
|
||||
/** 位置id */
|
||||
private Long positionId;
|
||||
|
||||
/** 厕所id */
|
||||
private Long toiletId;
|
||||
|
||||
/** 厂商类型0:厂商1;1:厂商2.... */
|
||||
private Integer factoryType;
|
||||
|
||||
/** 卡状态 */
|
||||
private String status;
|
||||
|
||||
/** 所属机构名称 */
|
||||
private String orgName;
|
||||
|
||||
/** 卡当前套餐名称 */
|
||||
private String iratePlanName;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package me.iiv.agent.mapper.m0;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import me.iiv.agent.entity.m0.HardwareView;
|
||||
|
||||
public interface HardwareViewMapper extends BaseMapper<HardwareView> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package me.iiv.agent.mapper.m0;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import me.iiv.agent.entity.m0.Networkcard;
|
||||
|
||||
public interface NetworkcardMapper extends BaseMapper<Networkcard> {
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
package me.iiv.agent.mcp.tool;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import me.iiv.agent.entity.m0.DistrictDistrictStrategy;
|
||||
import me.iiv.agent.entity.m0.HardwareView;
|
||||
import me.iiv.agent.entity.m0.Networkcard;
|
||||
import me.iiv.agent.entity.m0.User;
|
||||
import me.iiv.agent.mapper.m0.DistrictDistrictStrategyMapper;
|
||||
import me.iiv.agent.mapper.m0.HardwareViewMapper;
|
||||
import me.iiv.agent.mapper.m0.NetworkcardMapper;
|
||||
import me.iiv.agent.mcp.ToolObject;
|
||||
import me.iiv.agent.mcp.XqTool;
|
||||
import me.iiv.agent.permission.PermissionService;
|
||||
import me.iiv.agent.security.AuthContextHolder;
|
||||
import org.springframework.ai.tool.annotation.Tool;
|
||||
import org.springframework.ai.tool.annotation.ToolParam;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 出纸机查询工具,仿老系统 HardwareViewController /listByPage。
|
||||
* 富化修正老接口 N+1:名称/断纸状态走 Redis multiGet,物联网卡按页 IN 批量查。
|
||||
*/
|
||||
@Component
|
||||
public class HardwareTools implements ToolObject {
|
||||
|
||||
private static final int DEFAULT_ROWS = 20;
|
||||
private static final int MAX_ROWS = 100;
|
||||
|
||||
/** 老接口 Redis 字典 key(HardwareViewController /listByPage) */
|
||||
private static final String NAME_AGENT_PREFIX = "name:Agent:";
|
||||
private static final String NAME_PROPERTY_PREFIX = "name:Properyt:";
|
||||
private static final String NAME_DISTRICT_PREFIX = "name:District:";
|
||||
/** 断纸(缺纸工单)状态 key,老接口 :29 为纸状态业务类型;无 key 视为 0 */
|
||||
private static final String ASSIGNMENT_KEY_TEMPLATE = "Assignment:%s:29";
|
||||
|
||||
final HardwareViewMapper hardwareViewMapper;
|
||||
final NetworkcardMapper networkcardMapper;
|
||||
final DistrictDistrictStrategyMapper districtMapper;
|
||||
final PermissionService permissionService;
|
||||
final StringRedisTemplate redisTemplate;
|
||||
|
||||
public HardwareTools(HardwareViewMapper hardwareViewMapper, NetworkcardMapper networkcardMapper,
|
||||
DistrictDistrictStrategyMapper districtMapper,
|
||||
PermissionService permissionService, StringRedisTemplate redisTemplate) {
|
||||
this.hardwareViewMapper = hardwareViewMapper;
|
||||
this.networkcardMapper = networkcardMapper;
|
||||
this.districtMapper = districtMapper;
|
||||
this.permissionService = permissionService;
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
@XqTool(category = "hardware", kind = "read")
|
||||
@Tool(name = "hardware.list.query", description = "分页查询当前用户可见的出纸机列表,对应老系统 /hardware/listByPage。"
|
||||
+ "支持 snCode 模糊匹配,deviceType、status、online、lockMachineState、agentId、propertyId、districtId、buildingId、floor、imsi 精确匹配。"
|
||||
+ "page 默认 1,rows 默认 20、上限 100,固定按 id 倒序。"
|
||||
+ "agentName/propertyName/districtName 与断纸状态 piStatus 使用 Redis 批量富化,物联网卡信息按页批量查询。"
|
||||
+ "返回 hardware_view 全字段,不做字段级权限裁剪。")
|
||||
public Map<String, Object> hardwareListQuery(
|
||||
@ToolParam(required = false, description = "sn码,模糊匹配") String snCode,
|
||||
@ToolParam(required = false, description = "设备类型:0=厕纸机 1=擦手纸机") Integer deviceType,
|
||||
@ToolParam(required = false, description = "设备状态:1=在线 2=故障 3=离线") Integer status,
|
||||
@ToolParam(required = false, description = "上下线状态:1=上线 2=下线") Integer online,
|
||||
@ToolParam(required = false, description = "锁机状态:0=未锁机 1=已锁机") Integer lockMachineState,
|
||||
@ToolParam(required = false, description = "代理商 id,正整数,精确匹配") Long agentId,
|
||||
@ToolParam(required = false, description = "物业 id,正整数,精确匹配") Long propertyId,
|
||||
@ToolParam(required = false, description = "站点 id,正整数,精确匹配") Long districtId,
|
||||
@ToolParam(required = false, description = "楼 id,正整数,精确匹配") Long buildingId,
|
||||
@ToolParam(required = false, description = "楼层,精确匹配") String floor,
|
||||
@ToolParam(required = false, description = "imsi码,精确匹配") String imsi,
|
||||
@ToolParam(required = false, description = "页码,从 1 开始,默认 1") Integer page,
|
||||
@ToolParam(required = false, description = "每页条数,默认 20,上限 100") Integer rows) {
|
||||
validateEnum(deviceType, "deviceType", 0, 1);
|
||||
validateEnum(status, "status", 1, 2, 3);
|
||||
validateEnum(online, "online", 1, 2);
|
||||
validateEnum(lockMachineState, "lockMachineState", 0, 1);
|
||||
validatePositive(agentId, "agentId");
|
||||
validatePositive(propertyId, "propertyId");
|
||||
validatePositive(districtId, "districtId");
|
||||
validatePositive(buildingId, "buildingId");
|
||||
|
||||
User user = AuthContextHolder.get().getUser();
|
||||
int pageNo = page == null || page < 1 ? 1 : page;
|
||||
boolean truncated = rows != null && rows > MAX_ROWS;
|
||||
int pageSize = rows == null || rows < 1 ? DEFAULT_ROWS : Math.min(rows, MAX_ROWS);
|
||||
|
||||
LambdaQueryWrapper<HardwareView> wrapper = new LambdaQueryWrapper<HardwareView>()
|
||||
.eq(HardwareView::getSoftDelete, 0);
|
||||
if (StringUtils.hasText(snCode)) {
|
||||
wrapper.like(HardwareView::getSnCode, snCode);
|
||||
}
|
||||
if (deviceType != null) {
|
||||
wrapper.eq(HardwareView::getDeviceType, deviceType);
|
||||
}
|
||||
if (status != null) {
|
||||
wrapper.eq(HardwareView::getStatus, status);
|
||||
}
|
||||
if (online != null) {
|
||||
wrapper.eq(HardwareView::getOnline, online);
|
||||
}
|
||||
if (lockMachineState != null) {
|
||||
wrapper.eq(HardwareView::getLockMachineState, lockMachineState);
|
||||
}
|
||||
if (agentId != null) {
|
||||
wrapper.eq(HardwareView::getAgentId, agentId);
|
||||
}
|
||||
if (propertyId != null) {
|
||||
wrapper.eq(HardwareView::getPropertyId, propertyId);
|
||||
}
|
||||
if (districtId != null) {
|
||||
wrapper.eq(HardwareView::getDistrictId, districtId);
|
||||
}
|
||||
if (buildingId != null) {
|
||||
wrapper.eq(HardwareView::getBuildingId, buildingId);
|
||||
}
|
||||
if (StringUtils.hasText(floor)) {
|
||||
wrapper.eq(HardwareView::getFloor, floor);
|
||||
}
|
||||
if (StringUtils.hasText(imsi)) {
|
||||
wrapper.eq(HardwareView::getImsi, imsi);
|
||||
}
|
||||
permissionService.applyHardwareRowFilter(wrapper, user);
|
||||
|
||||
Long total = hardwareViewMapper.selectCount(wrapper);
|
||||
wrapper.orderByDesc(HardwareView::getId)
|
||||
.last("LIMIT " + (long) (pageNo - 1) * pageSize + "," + pageSize);
|
||||
List<HardwareView> list = hardwareViewMapper.selectList(wrapper);
|
||||
|
||||
List<Map<String, Object>> records = new ArrayList<>();
|
||||
Enrichment enrichment = enrich(list);
|
||||
for (HardwareView hardware : list) {
|
||||
records.add(toRecord(hardware, enrichment));
|
||||
}
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("total", total);
|
||||
result.put("page", pageNo);
|
||||
result.put("rows", pageSize);
|
||||
if (truncated) {
|
||||
result.put("notice", "rows 超过上限 100,已截断为 100");
|
||||
}
|
||||
result.put("records", records);
|
||||
return result;
|
||||
}
|
||||
|
||||
@XqTool(category = "hardware", kind = "read")
|
||||
@Tool(name = "hardware.detail.query", description = "按 snCode 或 id 查询当前用户可见的出纸机详情,二选一必填。"
|
||||
+ "返回 hardware_view 全字段 + agentName/propertyName/districtName、断纸状态 piStatus、"
|
||||
+ "物联网卡信息(factoryType/carrier/dataBalance/dataUsage/iccid/msisdn)+ 关联站点信息(districtName、省市区县、详细地址、联系人、楼座/楼层)。"
|
||||
+ "设备不存在或无权访问时返回 record=null 并在 notice 说明。")
|
||||
public Map<String, Object> hardwareDetailQuery(
|
||||
@ToolParam(required = false, description = "sn码,精确匹配;与 id 二选一必填") String snCode,
|
||||
@ToolParam(required = false, description = "设备 id,正整数,精确匹配;与 snCode 二选一必填") Long id) {
|
||||
if (!StringUtils.hasText(snCode) && id == null) {
|
||||
throw new IllegalArgumentException("snCode 与 id 至少填一个");
|
||||
}
|
||||
validatePositive(id, "id");
|
||||
|
||||
User user = AuthContextHolder.get().getUser();
|
||||
LambdaQueryWrapper<HardwareView> wrapper = new LambdaQueryWrapper<HardwareView>()
|
||||
.eq(HardwareView::getSoftDelete, 0);
|
||||
if (StringUtils.hasText(snCode)) {
|
||||
wrapper.eq(HardwareView::getSnCode, snCode);
|
||||
} else {
|
||||
wrapper.eq(HardwareView::getId, id);
|
||||
}
|
||||
permissionService.applyHardwareRowFilter(wrapper, user);
|
||||
HardwareView hardware = hardwareViewMapper.selectOne(wrapper);
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
if (hardware == null) {
|
||||
result.put("record", null);
|
||||
result.put("notice", "设备不存在或无权访问");
|
||||
return result;
|
||||
}
|
||||
Map<String, Object> record = toRecord(hardware, enrich(List.of(hardware)));
|
||||
record.put("site", siteInfo(hardware));
|
||||
result.put("record", record);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 站点信息:district 表现有字段(无经纬度/楼名字段);楼座/楼层取 hardware_view。 */
|
||||
private Map<String, Object> siteInfo(HardwareView hardware) {
|
||||
Map<String, Object> site = new LinkedHashMap<>();
|
||||
DistrictDistrictStrategy district = hardware.getDistrictId() == null
|
||||
? null : districtMapper.selectById(hardware.getDistrictId());
|
||||
site.put("districtId", hardware.getDistrictId());
|
||||
site.put("districtName", district == null ? null : district.getDistrictName());
|
||||
site.put("province", district == null ? null : district.getProvince());
|
||||
site.put("city", district == null ? null : district.getCity());
|
||||
site.put("countyseat", district == null ? null : district.getCountyseat());
|
||||
site.put("address", district == null ? null : district.getAddress());
|
||||
site.put("contact", district == null ? null : district.getContact());
|
||||
site.put("phone", district == null ? null : district.getPhone());
|
||||
site.put("buildingId", hardware.getBuildingId());
|
||||
site.put("floor", hardware.getFloor());
|
||||
return site;
|
||||
}
|
||||
|
||||
/** 批量富化上下文:Redis 名称/断纸状态 + 本页物联网卡,一次查询替代老接口 N+1。 */
|
||||
private Enrichment enrich(List<HardwareView> list) {
|
||||
Enrichment enrichment = new Enrichment();
|
||||
if (list.isEmpty()) {
|
||||
return enrichment;
|
||||
}
|
||||
enrichment.agentNames = redisNames(list, HardwareView::getAgentId, NAME_AGENT_PREFIX);
|
||||
enrichment.propertyNames = redisNames(list, HardwareView::getPropertyId, NAME_PROPERTY_PREFIX);
|
||||
enrichment.districtNames = redisNames(list, HardwareView::getDistrictId, NAME_DISTRICT_PREFIX);
|
||||
enrichment.piStatus = piStatus(list);
|
||||
enrichment.networkcards = networkcards(list);
|
||||
return enrichment;
|
||||
}
|
||||
|
||||
private Map<Long, String> redisNames(List<HardwareView> list, Function<HardwareView, Long> idGetter,
|
||||
String prefix) {
|
||||
List<Long> ids = list.stream().map(idGetter)
|
||||
.filter(Objects::nonNull).distinct().collect(Collectors.toList());
|
||||
if (ids.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
List<String> keys = ids.stream().map(id -> prefix + id).collect(Collectors.toList());
|
||||
List<String> values = redisTemplate.opsForValue().multiGet(keys);
|
||||
Map<Long, String> names = new LinkedHashMap<>();
|
||||
for (int i = 0; i < ids.size(); i++) {
|
||||
names.put(ids.get(i), values == null || i >= values.size() ? null : values.get(i));
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
private Map<String, Integer> piStatus(List<HardwareView> list) {
|
||||
List<String> snCodes = list.stream().map(HardwareView::getSnCode)
|
||||
.filter(StringUtils::hasText).distinct().collect(Collectors.toList());
|
||||
if (snCodes.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
List<String> keys = snCodes.stream()
|
||||
.map(sn -> String.format(ASSIGNMENT_KEY_TEMPLATE, sn)).collect(Collectors.toList());
|
||||
List<String> values = redisTemplate.opsForValue().multiGet(keys);
|
||||
Map<String, Integer> status = new LinkedHashMap<>();
|
||||
for (int i = 0; i < snCodes.size(); i++) {
|
||||
String value = values == null || i >= values.size() ? null : values.get(i);
|
||||
status.put(snCodes.get(i), value == null ? 0 : Integer.parseInt(value));
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/** 本页非空 imsi 一次性 IN 查 networkcard(老接口按条 limit 1,取首条语义保留)。 */
|
||||
private Map<String, Networkcard> networkcards(List<HardwareView> list) {
|
||||
List<String> imsIs = list.stream().map(HardwareView::getImsi)
|
||||
.filter(StringUtils::hasText).distinct().collect(Collectors.toList());
|
||||
if (imsIs.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
return networkcardMapper.selectList(new LambdaQueryWrapper<Networkcard>()
|
||||
.eq(Networkcard::getSoftDelete, 0)
|
||||
.in(Networkcard::getImsi, imsIs))
|
||||
.stream().collect(Collectors.toMap(Networkcard::getImsi, card -> card, (a, b) -> a));
|
||||
}
|
||||
|
||||
private Map<String, Object> toRecord(HardwareView h, Enrichment enrichment) {
|
||||
Map<String, Object> record = new LinkedHashMap<>();
|
||||
record.put("id", h.getId());
|
||||
record.put("no", h.getNo());
|
||||
record.put("name", h.getName());
|
||||
record.put("snCode", h.getSnCode());
|
||||
record.put("temSnCode", h.getTemSnCode());
|
||||
record.put("checkCode", h.getCheckCode());
|
||||
record.put("deviceType", h.getDeviceType());
|
||||
record.put("status", h.getStatus());
|
||||
record.put("online", h.getOnline());
|
||||
record.put("lockMachineState", h.getLockMachineState());
|
||||
record.put("agentId", h.getAgentId());
|
||||
record.put("agentName", mapGet(enrichment.agentNames, h.getAgentId()));
|
||||
record.put("propertyId", h.getPropertyId());
|
||||
record.put("propertyName", mapGet(enrichment.propertyNames, h.getPropertyId()));
|
||||
record.put("districtId", h.getDistrictId());
|
||||
record.put("districtName", mapGet(enrichment.districtNames, h.getDistrictId()));
|
||||
record.put("buildingId", h.getBuildingId());
|
||||
record.put("floor", h.getFloor());
|
||||
record.put("toiletId", h.getToiletId());
|
||||
record.put("positionId", h.getPositionId());
|
||||
record.put("position", h.getPosition());
|
||||
record.put("address", h.getAddress());
|
||||
record.put("version", h.getVersion());
|
||||
record.put("sysversion", h.getSysversion());
|
||||
record.put("firmwareVersion", h.getFirmwareVersion());
|
||||
record.put("voiceVersion", h.getVoiceVersion());
|
||||
record.put("advertiseVersion", h.getAdvertiseVersion());
|
||||
record.put("imsi", h.getImsi());
|
||||
record.put("simNo", h.getSimNo());
|
||||
Networkcard card = mapGet(enrichment.networkcards, h.getImsi());
|
||||
record.put("factoryType", card == null ? null : card.getFactoryType());
|
||||
record.put("carrier", card == null ? null : card.getCarrier());
|
||||
record.put("dataBalance", card == null ? null : card.getDataBalance());
|
||||
record.put("dataUsage", card == null ? null : card.getDataUsage());
|
||||
record.put("iccid", card == null ? null : card.getIccid());
|
||||
record.put("msisdn", card == null ? null : card.getMsisdn());
|
||||
record.put("piStatus", h.getSnCode() == null ? 0
|
||||
: enrichment.piStatus.getOrDefault(h.getSnCode(), 0));
|
||||
record.put("userId", h.getUserId());
|
||||
record.put("powerSupply", h.getPowerSupply());
|
||||
record.put("batteryTemp", h.getBatteryTemp());
|
||||
record.put("batteryCircle", h.getBatteryCircle());
|
||||
record.put("batteryFaultFlag", h.getBatteryFaultFlag());
|
||||
record.put("networkSignal", h.getNetworkSignal());
|
||||
record.put("current", h.getCurrent());
|
||||
record.put("voltage", h.getVoltage());
|
||||
record.put("paperSurplus", h.getPaperSurplus());
|
||||
record.put("paperCount", h.getPaperCount());
|
||||
record.put("paperState", h.getPaperState());
|
||||
record.put("powerState", h.getPowerState());
|
||||
record.put("positionState", h.getPositionState());
|
||||
record.put("useState", h.getUseState());
|
||||
record.put("paperType", h.getPaperType());
|
||||
record.put("paperNorms", h.getPaperNorms());
|
||||
record.put("senseLength", h.getSenseLength());
|
||||
record.put("scanLength", h.getScanLength());
|
||||
record.put("senseCount", h.getSenseCount());
|
||||
record.put("scanCount", h.getScanCount());
|
||||
record.put("rfid", h.getRfid());
|
||||
record.put("cleanRfidCount", h.getCleanRfidCount());
|
||||
record.put("installScenes", h.getInstallScenes());
|
||||
record.put("installMode", h.getInstallMode());
|
||||
record.put("poweredType", h.getPoweredType());
|
||||
record.put("bluetoothName", h.getBluetoothName());
|
||||
record.put("bluetoothMac", h.getBluetoothMac());
|
||||
record.put("chip", h.getChip());
|
||||
record.put("motherboard", h.getMotherboard());
|
||||
record.put("onlineTime", format(h.getOnlineTime()));
|
||||
record.put("warrantyPeriod", h.getWarrantyPeriod());
|
||||
record.put("warrantyExpirationTime", format(h.getWarrantyExpirationTime()));
|
||||
record.put("firstOnTime", format(h.getFirstOnTime()));
|
||||
record.put("runlMode", h.getRunlMode());
|
||||
record.put("lockState", h.getLockState());
|
||||
record.put("projectModel", h.getProjectModel());
|
||||
record.put("paperOutState", h.getPaperOutState());
|
||||
record.put("machineChipTime", format(h.getMachineChipTime()));
|
||||
record.put("machineChipCode", h.getMachineChipCode());
|
||||
record.put("currentChip", h.getCurrentChip());
|
||||
record.put("calibratFlag", h.getCalibratFlag());
|
||||
record.put("fullRollFlag", h.getFullRollFlag());
|
||||
record.put("middleFlag", h.getMiddleFlag());
|
||||
record.put("lowerFlag", h.getLowerFlag());
|
||||
record.put("paperLockFlag", h.getPaperLockFlag());
|
||||
record.put("paperLockCount", h.getPaperLockCount());
|
||||
record.put("lowerFaultFlag", h.getLowerFaultFlag());
|
||||
record.put("middleFaultFlag", h.getMiddleFaultFlag());
|
||||
record.put("illegalPaperDisableState", h.getIllegalPaperDisableState());
|
||||
record.put("nfcFlag", h.getNfcFlag());
|
||||
record.put("nfcId", h.getNfcId());
|
||||
record.put("timerVoiceFlag", h.getTimerVoiceFlag());
|
||||
record.put("timerVoiceTiming", h.getTimerVoiceTiming());
|
||||
record.put("timerPaperoutFlag", h.getTimerPaperoutFlag());
|
||||
record.put("timerPaperoutTiming", h.getTimerPaperoutTiming());
|
||||
record.put("timerPaperoutLength", h.getTimerPaperoutLength());
|
||||
record.put("prdMemo", h.getPrdMemo());
|
||||
record.put("runMemo", h.getRunMemo());
|
||||
record.put("rentFlag", h.getRentFlag());
|
||||
record.put("createId", h.getCreateId());
|
||||
record.put("createTime", format(h.getCreateTime()));
|
||||
record.put("modifyTime", format(h.getModifyTime()));
|
||||
record.put("memo", h.getMemo());
|
||||
return record;
|
||||
}
|
||||
|
||||
private static final class Enrichment {
|
||||
Map<Long, String> agentNames = Map.of();
|
||||
Map<Long, String> propertyNames = Map.of();
|
||||
Map<Long, String> districtNames = Map.of();
|
||||
Map<String, Integer> piStatus = Map.of();
|
||||
Map<String, Networkcard> networkcards = Map.of();
|
||||
}
|
||||
|
||||
private static <K, V> V mapGet(Map<K, V> map, K key) {
|
||||
return key == null ? null : map.get(key);
|
||||
}
|
||||
|
||||
private static String format(Date value) {
|
||||
return value == null ? null : new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(value);
|
||||
}
|
||||
|
||||
private static void validatePositive(Long value, String name) {
|
||||
if (value != null && value < 1) {
|
||||
throw new IllegalArgumentException(name + " 必须为正整数");
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateEnum(Integer value, String name, int... allowed) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
for (int candidate : allowed) {
|
||||
if (value == candidate) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
String options = Arrays.stream(allowed).mapToObj(String::valueOf)
|
||||
.collect(Collectors.joining("、"));
|
||||
throw new IllegalArgumentException(name + " 仅支持 " + options);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import me.iiv.agent.entity.m0.Agent;
|
||||
import me.iiv.agent.entity.m0.AgentManageDistrictAuth;
|
||||
import me.iiv.agent.entity.m0.Authority;
|
||||
import me.iiv.agent.entity.m0.DistrictDistrictStrategy;
|
||||
import me.iiv.agent.entity.m0.HardwareView;
|
||||
import me.iiv.agent.entity.m0.Properyt;
|
||||
import me.iiv.agent.entity.m0.User;
|
||||
import me.iiv.agent.entity.m1.MallGoodsOrder;
|
||||
@@ -301,12 +302,28 @@ public class PermissionService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 站点行权限:1:1 对齐老接口 districtlistByPage 使用的
|
||||
* getByIsAgent(..., propertyFlag=true, className="property_id")。
|
||||
* 行权限范围解析结果(老 getByIsAgent propertyFlag=true 变体的公共中间态)。
|
||||
* headquarters=true 表示 isagent!=1(总部),调用方不加任何过滤。
|
||||
* salesPropertyIds 非 null 表示销售部门非负责人互斥分支:只按物业 id 集合过滤
|
||||
* (空集合已归一为 [0],保证查不到数据)。
|
||||
*/
|
||||
public void applyDistrictRowFilter(LambdaQueryWrapper<DistrictDistrictStrategy> wrapper, User user) {
|
||||
private static final class RowScope {
|
||||
boolean headquarters;
|
||||
List<Long> ids;
|
||||
List<Long> salesPropertyIds;
|
||||
List<Long> managedPropertyIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析当前用户的行权限范围,district / hardware 等 propertyFlag=true、
|
||||
* className="property_id" 变体共用(老 WrapperUtils.java:679-764)。
|
||||
* 返回 headquarters 标记的 scope;调用方各自把结果落到自己的 wrapper 字段上。
|
||||
*/
|
||||
private RowScope resolveRowScope(User user) {
|
||||
RowScope scope = new RowScope();
|
||||
if (!isAgentAuthority(user.getAuthorityId())) {
|
||||
return;
|
||||
scope.headquarters = true;
|
||||
return scope;
|
||||
}
|
||||
if (user.getAgentId() == null) {
|
||||
throw new IllegalStateException("代理商用户未关联代理商,拒绝访问业务数据");
|
||||
@@ -315,6 +332,7 @@ public class PermissionService {
|
||||
List<Long> ids = new ArrayList<>(subordinateAndSelfIds(user));
|
||||
List<WarehouseAgentAuth> agentlist = managedAuths(user.getId());
|
||||
ids.addAll(agentlist.stream().map(WarehouseAgentAuth::getManagerAgentId).collect(Collectors.toList()));
|
||||
scope.ids = ids;
|
||||
|
||||
if (isSalesNonLeader(user)) {
|
||||
List<Long> propertyIds = properytMapper.selectList(new LambdaQueryWrapper<Properyt>()
|
||||
@@ -332,17 +350,54 @@ public class PermissionService {
|
||||
.stream().map(Properyt::getId).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
wrapper.in(DistrictDistrictStrategy::getPropertyId,
|
||||
propertyIds.isEmpty() ? List.of(0L) : propertyIds);
|
||||
return;
|
||||
scope.salesPropertyIds = propertyIds.isEmpty() ? List.of(0L) : propertyIds;
|
||||
return scope;
|
||||
}
|
||||
|
||||
List<Long> managedPropertyIds = managedPropertyIds(ids);
|
||||
if (!managedPropertyIds.isEmpty()) {
|
||||
wrapper.and(w -> w.in(DistrictDistrictStrategy::getAgentId, ids)
|
||||
.or().in(DistrictDistrictStrategy::getPropertyId, managedPropertyIds));
|
||||
scope.managedPropertyIds = managedPropertyIds(ids);
|
||||
return scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* 站点行权限:1:1 对齐老接口 districtlistByPage 使用的
|
||||
* getByIsAgent(..., propertyFlag=true, className="property_id")。
|
||||
*/
|
||||
public void applyDistrictRowFilter(LambdaQueryWrapper<DistrictDistrictStrategy> wrapper, User user) {
|
||||
RowScope scope = resolveRowScope(user);
|
||||
if (scope.headquarters) {
|
||||
return;
|
||||
}
|
||||
if (scope.salesPropertyIds != null) {
|
||||
wrapper.in(DistrictDistrictStrategy::getPropertyId, scope.salesPropertyIds);
|
||||
return;
|
||||
}
|
||||
if (!scope.managedPropertyIds.isEmpty()) {
|
||||
wrapper.and(w -> w.in(DistrictDistrictStrategy::getAgentId, scope.ids)
|
||||
.or().in(DistrictDistrictStrategy::getPropertyId, scope.managedPropertyIds));
|
||||
} else {
|
||||
wrapper.in(DistrictDistrictStrategy::getAgentId, ids);
|
||||
wrapper.in(DistrictDistrictStrategy::getAgentId, scope.ids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 出纸机(hardware_view)行权限:与站点同一 getByIsAgent 变体
|
||||
* (propertyFlag=true, className="property_id",老 HardwareViewController /listByPage),
|
||||
* 仅落到 HardwareView 的 agent_id / property_id 字段上。
|
||||
*/
|
||||
public void applyHardwareRowFilter(LambdaQueryWrapper<HardwareView> wrapper, User user) {
|
||||
RowScope scope = resolveRowScope(user);
|
||||
if (scope.headquarters) {
|
||||
return;
|
||||
}
|
||||
if (scope.salesPropertyIds != null) {
|
||||
wrapper.in(HardwareView::getPropertyId, scope.salesPropertyIds);
|
||||
return;
|
||||
}
|
||||
if (!scope.managedPropertyIds.isEmpty()) {
|
||||
wrapper.and(w -> w.in(HardwareView::getAgentId, scope.ids)
|
||||
.or().in(HardwareView::getPropertyId, scope.managedPropertyIds));
|
||||
} else {
|
||||
wrapper.in(HardwareView::getAgentId, scope.ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,283 @@
|
||||
package me.iiv.agent.mcp.tool;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import me.iiv.agent.entity.m0.DistrictDistrictStrategy;
|
||||
import me.iiv.agent.entity.m0.HardwareView;
|
||||
import me.iiv.agent.entity.m0.Networkcard;
|
||||
import me.iiv.agent.entity.m0.User;
|
||||
import me.iiv.agent.mapper.m0.DistrictDistrictStrategyMapper;
|
||||
import me.iiv.agent.mapper.m0.HardwareViewMapper;
|
||||
import me.iiv.agent.mapper.m0.NetworkcardMapper;
|
||||
import me.iiv.agent.permission.PermissionService;
|
||||
import me.iiv.agent.security.AuthContext;
|
||||
import me.iiv.agent.security.AuthContextHolder;
|
||||
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class HardwareToolsTest {
|
||||
|
||||
@Mock
|
||||
HardwareViewMapper hardwareViewMapper;
|
||||
@Mock
|
||||
NetworkcardMapper networkcardMapper;
|
||||
@Mock
|
||||
DistrictDistrictStrategyMapper districtMapper;
|
||||
@Mock
|
||||
PermissionService permissionService;
|
||||
@Mock
|
||||
StringRedisTemplate redisTemplate;
|
||||
|
||||
HardwareTools tools;
|
||||
User user;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""),
|
||||
HardwareView.class);
|
||||
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""),
|
||||
Networkcard.class);
|
||||
tools = new HardwareTools(hardwareViewMapper, networkcardMapper, districtMapper,
|
||||
permissionService, redisTemplate);
|
||||
user = new User();
|
||||
user.setId(7L);
|
||||
AuthContextHolder.set(new AuthContext(null, user));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
AuthContextHolder.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void listBuildsLegacyFiltersAndTruncatesRows() {
|
||||
HardwareView hardware = new HardwareView();
|
||||
hardware.setId(9L);
|
||||
hardware.setSnCode("D2AB001");
|
||||
hardware.setAgentId(12L);
|
||||
hardware.setPropertyId(11L);
|
||||
hardware.setDistrictId(5L);
|
||||
hardware.setImsi("460001234567890");
|
||||
when(hardwareViewMapper.selectCount(any())).thenReturn(1L);
|
||||
when(hardwareViewMapper.selectList(any())).thenReturn(List.of(hardware));
|
||||
|
||||
ValueOperations<String, String> valueOperations = mock(ValueOperations.class);
|
||||
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
|
||||
// name:Agent:12 / name:Properyt:11 / name:District:5 / Assignment:D2AB001:29
|
||||
when(valueOperations.multiGet(anyList())).thenAnswer(invocation -> {
|
||||
List<String> keys = invocation.getArgument(0);
|
||||
return keys.stream().map(key -> switch (key) {
|
||||
case "name:Agent:12" -> "测试代理";
|
||||
case "name:Properyt:11" -> "测试物业";
|
||||
case "name:District:5" -> "测试站点";
|
||||
case "Assignment:D2AB001:29" -> "1";
|
||||
default -> null;
|
||||
}).collect(Collectors.toList());
|
||||
});
|
||||
Networkcard card = new Networkcard();
|
||||
card.setImsi("460001234567890");
|
||||
card.setFactoryType(1);
|
||||
card.setCarrier("移动");
|
||||
card.setDataBalance(new BigDecimal("512.5"));
|
||||
card.setDataUsage(new BigDecimal("128.0"));
|
||||
card.setIccid("8986001234");
|
||||
card.setMsisdn("14412345678");
|
||||
when(networkcardMapper.selectList(any())).thenReturn(List.of(card));
|
||||
|
||||
Map<String, Object> result = tools.hardwareListQuery(
|
||||
"D2AB", 0, 1, 1, 0, 12L, 11L, 5L, 3L, "3F", "460001234567890", 1, 500);
|
||||
|
||||
assertEquals(1, result.get("page"));
|
||||
assertEquals(100, result.get("rows"));
|
||||
assertEquals("rows 超过上限 100,已截断为 100", result.get("notice"));
|
||||
|
||||
// 过滤拼装与老接口一一对应
|
||||
ArgumentCaptor<LambdaQueryWrapper<HardwareView>> captor = ArgumentCaptor.forClass(LambdaQueryWrapper.class);
|
||||
verify(hardwareViewMapper).selectList(captor.capture());
|
||||
String sql = captor.getValue().getSqlSegment();
|
||||
assertTrue(sql.contains("sn_code LIKE"));
|
||||
assertTrue(sql.contains("device_type ="));
|
||||
assertTrue(sql.contains("status ="));
|
||||
assertTrue(sql.contains("online ="));
|
||||
assertTrue(sql.contains("lock_machine_state ="));
|
||||
assertTrue(sql.contains("agent_id ="));
|
||||
assertTrue(sql.contains("property_id ="));
|
||||
assertTrue(sql.contains("district_id ="));
|
||||
assertTrue(sql.contains("building_id ="));
|
||||
assertTrue(sql.contains("floor ="));
|
||||
assertTrue(sql.contains("imsi ="));
|
||||
assertTrue(sql.contains("soft_delete ="));
|
||||
assertTrue(captor.getValue().getSqlSegment().contains("ORDER BY id DESC"));
|
||||
verify(permissionService).applyHardwareRowFilter(any(), any());
|
||||
|
||||
// 富化:名称、断纸状态、物联网卡
|
||||
Map<String, Object> record = ((List<Map<String, Object>>) result.get("records")).get(0);
|
||||
assertEquals("测试代理", record.get("agentName"));
|
||||
assertEquals("测试物业", record.get("propertyName"));
|
||||
assertEquals("测试站点", record.get("districtName"));
|
||||
assertEquals(1, record.get("piStatus"));
|
||||
assertEquals(1, record.get("factoryType"));
|
||||
assertEquals("移动", record.get("carrier"));
|
||||
assertEquals(new BigDecimal("512.5"), record.get("dataBalance"));
|
||||
assertEquals(new BigDecimal("128.0"), record.get("dataUsage"));
|
||||
assertEquals("8986001234", record.get("iccid"));
|
||||
assertEquals("14412345678", record.get("msisdn"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void listDefaultsPiStatusToZeroWhenAssignmentKeyMissing() {
|
||||
HardwareView hardware = new HardwareView();
|
||||
hardware.setId(9L);
|
||||
hardware.setSnCode("D2AB002");
|
||||
when(hardwareViewMapper.selectCount(any())).thenReturn(1L);
|
||||
when(hardwareViewMapper.selectList(any())).thenReturn(List.of(hardware));
|
||||
|
||||
ValueOperations<String, String> valueOperations = mock(ValueOperations.class);
|
||||
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
|
||||
when(valueOperations.multiGet(anyList())).thenReturn(List.of());
|
||||
|
||||
Map<String, Object> result = tools.hardwareListQuery(
|
||||
null, null, null, null, null, null, null, null, null, null, null, 1, 20);
|
||||
|
||||
Map<String, Object> record = ((List<Map<String, Object>>) result.get("records")).get(0);
|
||||
assertEquals(0, record.get("piStatus"));
|
||||
assertNull(record.get("agentName"));
|
||||
// 无 imsi 不查物联网卡
|
||||
verify(networkcardMapper, never()).selectList(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailRequiresSnCodeOrId() {
|
||||
assertThrows(IllegalArgumentException.class, () -> tools.hardwareDetailQuery(null, null));
|
||||
assertThrows(IllegalArgumentException.class, () -> tools.hardwareDetailQuery(" ", 0L));
|
||||
verifyNoInteractions(hardwareViewMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailReturnsNullRecordWithNoticeWhenMissingOrUnauthorized() {
|
||||
when(hardwareViewMapper.selectOne(any())).thenReturn(null);
|
||||
|
||||
Map<String, Object> result = tools.hardwareDetailQuery("D2AB404", null);
|
||||
|
||||
assertNull(result.get("record"));
|
||||
assertEquals("设备不存在或无权访问", result.get("notice"));
|
||||
verify(permissionService).applyHardwareRowFilter(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void detailBySnCodeReturnsFullRecordWithSiteAndEnrichment() {
|
||||
HardwareView hardware = new HardwareView();
|
||||
hardware.setId(9L);
|
||||
hardware.setSnCode("D2AB001");
|
||||
hardware.setDeviceType(0);
|
||||
hardware.setStatus(1);
|
||||
hardware.setOnline(1);
|
||||
hardware.setLockMachineState(0);
|
||||
hardware.setSysversion("v1.2.3");
|
||||
hardware.setFirmwareVersion("hw-9");
|
||||
hardware.setImsi("460001234567890");
|
||||
hardware.setDistrictId(5L);
|
||||
hardware.setBuildingId(3L);
|
||||
hardware.setFloor("3F");
|
||||
when(hardwareViewMapper.selectOne(any())).thenReturn(hardware);
|
||||
|
||||
ValueOperations<String, String> valueOperations = mock(ValueOperations.class);
|
||||
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
|
||||
when(valueOperations.multiGet(anyList())).thenReturn(List.of());
|
||||
when(networkcardMapper.selectList(any())).thenReturn(List.of());
|
||||
|
||||
DistrictDistrictStrategy district = new DistrictDistrictStrategy();
|
||||
district.setId(5L);
|
||||
district.setDistrictName("测试站点");
|
||||
district.setProvince("广东省");
|
||||
district.setCity("深圳市");
|
||||
district.setCountyseat("南山区");
|
||||
district.setAddress("科技园");
|
||||
district.setContact("张三");
|
||||
district.setPhone("13800000000");
|
||||
when(districtMapper.selectById(5L)).thenReturn(district);
|
||||
|
||||
Map<String, Object> result = tools.hardwareDetailQuery("D2AB001", null);
|
||||
|
||||
Map<String, Object> record = (Map<String, Object>) result.get("record");
|
||||
assertEquals("D2AB001", record.get("snCode"));
|
||||
assertEquals(0, record.get("deviceType"));
|
||||
assertEquals(1, record.get("status"));
|
||||
assertEquals(1, record.get("online"));
|
||||
assertEquals(0, record.get("lockMachineState"));
|
||||
assertEquals("v1.2.3", record.get("sysversion"));
|
||||
assertEquals("hw-9", record.get("firmwareVersion"));
|
||||
assertEquals(0, record.get("piStatus"));
|
||||
|
||||
Map<String, Object> site = (Map<String, Object>) record.get("site");
|
||||
assertEquals(5L, site.get("districtId"));
|
||||
assertEquals("测试站点", site.get("districtName"));
|
||||
assertEquals("广东省", site.get("province"));
|
||||
assertEquals("深圳市", site.get("city"));
|
||||
assertEquals("南山区", site.get("countyseat"));
|
||||
assertEquals("科技园", site.get("address"));
|
||||
assertEquals("张三", site.get("contact"));
|
||||
assertEquals("13800000000", site.get("phone"));
|
||||
assertEquals(3L, site.get("buildingId"));
|
||||
assertEquals("3F", site.get("floor"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void listRejectsUnknownEnumsBeforeQuerying() {
|
||||
assertThrows(IllegalArgumentException.class, () -> tools.hardwareListQuery(
|
||||
null, 2, null, null, null, null, null, null, null, null, null, 1, 20));
|
||||
assertThrows(IllegalArgumentException.class, () -> tools.hardwareListQuery(
|
||||
null, null, 0, null, null, null, null, null, null, null, null, 1, 20));
|
||||
assertThrows(IllegalArgumentException.class, () -> tools.hardwareListQuery(
|
||||
null, null, null, 3, null, null, null, null, null, null, null, 1, 20));
|
||||
assertThrows(IllegalArgumentException.class, () -> tools.hardwareListQuery(
|
||||
null, null, null, null, 2, null, null, null, null, null, null, 1, 20));
|
||||
verifyNoInteractions(hardwareViewMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void springAiPublishesBothReviewedToolSchemas() {
|
||||
var callbacks = org.springframework.ai.tool.method.MethodToolCallbackProvider.builder()
|
||||
.toolObjects(tools).build().getToolCallbacks();
|
||||
Map<String, String> schemas = Arrays.stream(callbacks).collect(Collectors.toMap(
|
||||
callback -> callback.getToolDefinition().name(),
|
||||
callback -> callback.getToolDefinition().inputSchema()));
|
||||
|
||||
assertEquals(2, schemas.size());
|
||||
assertTrue(schemas.containsKey("hardware.list.query"));
|
||||
assertTrue(schemas.containsKey("hardware.detail.query"));
|
||||
assertTrue(schemas.get("hardware.list.query").contains("lockMachineState"));
|
||||
assertTrue(schemas.get("hardware.detail.query").contains("snCode"));
|
||||
}
|
||||
}
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
package me.iiv.agent.permission;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import me.iiv.agent.entity.m0.Agent;
|
||||
import me.iiv.agent.entity.m0.AgentManageDistrictAuth;
|
||||
import me.iiv.agent.entity.m0.Authority;
|
||||
import me.iiv.agent.entity.m0.HardwareView;
|
||||
import me.iiv.agent.entity.m0.Properyt;
|
||||
import me.iiv.agent.entity.m0.User;
|
||||
import me.iiv.agent.entity.m2.WarehouseAgentAuth;
|
||||
import me.iiv.agent.mapper.m0.AgentManageDistrictAuthMapper;
|
||||
import me.iiv.agent.mapper.m0.AgentMapper;
|
||||
import me.iiv.agent.mapper.m0.AuthorityMapper;
|
||||
import me.iiv.agent.mapper.m0.ProperytMapper;
|
||||
import me.iiv.agent.mapper.m2.WarehouseAgentAuthMapper;
|
||||
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.redis.core.HashOperations;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/** PermissionService.applyHardwareRowFilter 直接单测:hardware_view 行级权限各分支(与站点同一 getByIsAgent 变体)。 */
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PermissionServiceHardwareRowFilterTest {
|
||||
|
||||
@Mock
|
||||
AuthorityMapper authorityMapper;
|
||||
@Mock
|
||||
AgentMapper agentMapper;
|
||||
@Mock
|
||||
WarehouseAgentAuthMapper warehouseAgentAuthMapper;
|
||||
@Mock
|
||||
AgentManageDistrictAuthMapper agentManageDistrictAuthMapper;
|
||||
@Mock
|
||||
ProperytMapper properytMapper;
|
||||
@Mock
|
||||
StringRedisTemplate redisTemplate;
|
||||
@Mock
|
||||
HashOperations<String, Object, Object> hashOperations;
|
||||
|
||||
PermissionService permissionService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""),
|
||||
HardwareView.class);
|
||||
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""),
|
||||
Properyt.class);
|
||||
permissionService = new PermissionService(authorityMapper, agentMapper,
|
||||
warehouseAgentAuthMapper, agentManageDistrictAuthMapper, properytMapper, redisTemplate);
|
||||
}
|
||||
|
||||
@Test
|
||||
void headquartersHardwareFilterAddsNoBoundary() {
|
||||
User user = user(46L, 1L, null);
|
||||
when(authorityMapper.selectById(1L)).thenReturn(authority(1L, 0));
|
||||
|
||||
LambdaQueryWrapper<HardwareView> wrapper = new LambdaQueryWrapper<>();
|
||||
permissionService.applyHardwareRowFilter(wrapper, user);
|
||||
|
||||
assertTrue(wrapper.getSqlSegment().isBlank());
|
||||
}
|
||||
|
||||
@Test
|
||||
void agentHardwareFilterUsesSelfSubordinateAndManagedAgents() {
|
||||
User user = user(1000L, 108L, 1L);
|
||||
when(authorityMapper.selectById(108L)).thenReturn(authority(108L, 1));
|
||||
Agent subordinate = new Agent();
|
||||
subordinate.setId(2L);
|
||||
when(agentMapper.selectList(any())).thenReturn(List.of(subordinate));
|
||||
WarehouseAgentAuth managedAuth = new WarehouseAgentAuth();
|
||||
managedAuth.setManagerAgentId(3L);
|
||||
when(warehouseAgentAuthMapper.selectList(any())).thenReturn(List.of(managedAuth));
|
||||
when(agentManageDistrictAuthMapper.selectList(any())).thenReturn(List.of());
|
||||
|
||||
LambdaQueryWrapper<HardwareView> wrapper = new LambdaQueryWrapper<>();
|
||||
permissionService.applyHardwareRowFilter(wrapper, user);
|
||||
|
||||
String sql = wrapper.getSqlSegment();
|
||||
Collection<Object> values = wrapper.getParamNameValuePairs().values();
|
||||
assertTrue(sql.contains("agent_id IN"));
|
||||
assertFalse(sql.contains("property_id"));
|
||||
assertTrue(values.contains(1L));
|
||||
assertTrue(values.contains(2L));
|
||||
assertTrue(values.contains(3L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void agentHardwareFilterAddsManagedPropertyOrBranch() {
|
||||
User user = user(1000L, 108L, 1L);
|
||||
when(authorityMapper.selectById(108L)).thenReturn(authority(108L, 1));
|
||||
when(agentMapper.selectList(any())).thenReturn(List.of());
|
||||
when(warehouseAgentAuthMapper.selectList(any())).thenReturn(List.of());
|
||||
AgentManageDistrictAuth districtAuth = new AgentManageDistrictAuth();
|
||||
districtAuth.setPropertyId(44L);
|
||||
when(agentManageDistrictAuthMapper.selectList(any())).thenReturn(List.of(districtAuth));
|
||||
|
||||
LambdaQueryWrapper<HardwareView> wrapper = new LambdaQueryWrapper<>();
|
||||
permissionService.applyHardwareRowFilter(wrapper, user);
|
||||
|
||||
String sql = wrapper.getSqlSegment();
|
||||
Collection<Object> values = wrapper.getParamNameValuePairs().values();
|
||||
assertTrue(sql.contains("agent_id IN"));
|
||||
assertTrue(sql.contains("property_id IN"));
|
||||
assertTrue(sql.contains("OR"));
|
||||
assertTrue(values.contains(1L));
|
||||
assertTrue(values.contains(44L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void salesNonLeaderHardwareFilterUsesOwnCreatedPropertyIds() {
|
||||
User user = user(1000L, 108L, 1L);
|
||||
user.setDeptId(5L);
|
||||
user.setPostId(7L);
|
||||
when(authorityMapper.selectById(108L)).thenReturn(authority(108L, 1));
|
||||
when(agentMapper.selectList(any())).thenReturn(List.of());
|
||||
when(warehouseAgentAuthMapper.selectList(any())).thenReturn(List.of());
|
||||
when(redisTemplate.opsForHash()).thenReturn(hashOperations);
|
||||
when(hashOperations.get("Dictionary", "authDeptId")).thenReturn(",5,");
|
||||
when(hashOperations.get("Dictionary", "authDeptPostId")).thenReturn("8,");
|
||||
Properyt property = new Properyt();
|
||||
property.setId(55L);
|
||||
when(properytMapper.selectList(any())).thenReturn(List.of(property));
|
||||
|
||||
LambdaQueryWrapper<HardwareView> wrapper = new LambdaQueryWrapper<>();
|
||||
permissionService.applyHardwareRowFilter(wrapper, user);
|
||||
|
||||
String sql = wrapper.getSqlSegment();
|
||||
assertTrue(sql.contains("property_id IN"));
|
||||
assertFalse(sql.contains("agent_id"));
|
||||
assertTrue(wrapper.getParamNameValuePairs().values().contains(55L));
|
||||
verifyNoInteractions(agentManageDistrictAuthMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void salesNonLeaderWithoutVisiblePropertiesFiltersToZero() {
|
||||
User user = user(1000L, 108L, 1L);
|
||||
user.setDeptId(5L);
|
||||
user.setPostId(7L);
|
||||
when(authorityMapper.selectById(108L)).thenReturn(authority(108L, 1));
|
||||
when(agentMapper.selectList(any())).thenReturn(List.of());
|
||||
when(warehouseAgentAuthMapper.selectList(any())).thenReturn(List.of());
|
||||
when(redisTemplate.opsForHash()).thenReturn(hashOperations);
|
||||
when(hashOperations.get("Dictionary", "authDeptId")).thenReturn(",5,");
|
||||
when(hashOperations.get("Dictionary", "authDeptPostId")).thenReturn("8,");
|
||||
when(properytMapper.selectList(any())).thenReturn(List.of());
|
||||
|
||||
LambdaQueryWrapper<HardwareView> wrapper = new LambdaQueryWrapper<>();
|
||||
permissionService.applyHardwareRowFilter(wrapper, user);
|
||||
|
||||
String sql = wrapper.getSqlSegment();
|
||||
assertTrue(sql.contains("property_id IN"));
|
||||
assertFalse(sql.contains("agent_id"));
|
||||
assertTrue(wrapper.getParamNameValuePairs().values().contains(0L));
|
||||
verifyNoInteractions(agentManageDistrictAuthMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void agentUserWithoutAgentIdIsDeniedForHardwareFilter() {
|
||||
User user = user(1000L, 108L, null);
|
||||
when(authorityMapper.selectById(108L)).thenReturn(authority(108L, 1));
|
||||
|
||||
assertThrows(IllegalStateException.class,
|
||||
() -> permissionService.applyHardwareRowFilter(new LambdaQueryWrapper<>(), user));
|
||||
}
|
||||
|
||||
private static Authority authority(Long id, int isAgent) {
|
||||
Authority authority = new Authority();
|
||||
authority.setId(id);
|
||||
authority.setIsagent(isAgent);
|
||||
authority.setSoftDelete(0);
|
||||
return authority;
|
||||
}
|
||||
|
||||
private static User user(Long id, Long authorityId, Long agentId) {
|
||||
User user = new User();
|
||||
user.setId(id);
|
||||
user.setAuthorityId(authorityId);
|
||||
user.setAgentId(agentId);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user