diff --git a/xiaoqu-agent-service/deploy/.env.example b/xiaoqu-agent-service/deploy/.env.example index f8d569b..a47b238 100644 --- a/xiaoqu-agent-service/deploy/.env.example +++ b/xiaoqu-agent-service/deploy/.env.example @@ -2,9 +2,9 @@ # 数据库/Redis 连接信息已明文写在 application-dev.yml,无需在此配置 # 后台管理员首次引导(仅首次启动且 admin_user 为空时建号,之后可删除) -APP_ADMIN_BOOTSTRAP_ACCOUNT=admin -APP_ADMIN_BOOTSTRAP_PASSWORD= -# APP_ADMIN_BOOTSTRAP_NAME=系统管理员 +ADMIN_BOOTSTRAP_ACCOUNT=admin +ADMIN_BOOTSTRAP_PASSWORD= +# ADMIN_BOOTSTRAP_NAME=系统管理员 # 默认拒绝为非空库自动建基线;仅已有 V1 库首次接入时临时改为 true SVC_FLYWAY_BASELINE_ON_MIGRATE=false diff --git a/xiaoqu-agent-service/deploy/test-up.sh b/xiaoqu-agent-service/deploy/test-up.sh new file mode 100755 index 0000000..4f384d5 --- /dev/null +++ b/xiaoqu-agent-service/deploy/test-up.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# 本机即测试环境:一键 构建 → 重启 → 刷新 scope 工具绑定 → 自检 +# 用法: +# ./deploy/test-up.sh # 全量(含 Maven 构建,约 7 分钟) +# ./deploy/test-up.sh --skip-build # 跳过构建,直接重启+刷新绑定 +# 首次运行会自动生成 deploy/.env.dev(含随机 admin 初始密码,注意看输出) +set -euo pipefail + +APP_HOME="$(cd "$(dirname "$0")/.." && pwd)" +DEPLOY="$APP_HOME/deploy/deploy.sh" +ENV_FILE="$APP_HOME/deploy/.env.dev" +SEED_SQL="$APP_HOME/sql/seed-dev.sql" +MYSQL="${MYSQL_BIN:-mysql}" + +# dev svc 库(与 application-dev.yml 保持一致) +SVC_DB_HOST=192.168.1.181 +SVC_DB_USER=root +SVC_DB_PASSWORD=kaixinjiuhao +SVC_DB_NAME=xiaoqu_agent_svc_d + +# 1. 首启引导配置(不存在才生成;admin 仅首次启动且 admin_user 为空时建号) +if [ ! -f "$ENV_FILE" ]; then + ADMIN_PWD="Dev@$(openssl rand -hex 6)" + cat > "$ENV_FILE" < /dev/null +echo "[OK] scope 工具绑定已刷新(operation)" + +# 5. 自检:后台登录接口存活(不验密码对错,只验服务可登录) +code=$(curl -s --noproxy '*' -o /dev/null -w "%{http_code}" -m 10 -X POST \ + http://127.0.0.1:8088/api/admin/auth/login \ + -H 'Content-Type: application/json' \ + -d '{"account":"__selfcheck__","password":"__selfcheck__"}') +if [ "$code" = "401" ] || [ "$code" = "200" ]; then + echo "[OK] 登录接口存活 (HTTP $code)" +else + echo "[WARN] 登录接口异常 (HTTP $code),请查看: $DEPLOY log" >&2 +fi + +echo +echo "本机测试环境就绪: http://localhost:8088/ (admin 账号见 deploy/.env.dev)" +echo "日常命令: $DEPLOY {start|stop|restart|status|log}" diff --git a/xiaoqu-agent-service/sql/seed-dev.sql b/xiaoqu-agent-service/sql/seed-dev.sql index 0f4dae1..9a6bb82 100644 --- a/xiaoqu-agent-service/sql/seed-dev.sql +++ b/xiaoqu-agent-service/sql/seed-dev.sql @@ -14,10 +14,10 @@ INSERT INTO agent_scope (name, code, memo, status, create_id) SELECT '运营', 'operation', 'dev 种子:运营作用域', 1, 0 WHERE NOT EXISTS (SELECT 1 FROM agent_scope WHERE code = 'operation'); --- 作用域-工具:sys.ping / agent.count / agent.list.query / property.list.query +-- 作用域-工具:sys.ping / agent.count / agent.list.query / property.list.query / agent.detail.query / property.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 = 'operation' AND t.name IN ('sys.ping', 'agent.count', 'agent.list.query', 'property.list.query') +WHERE s.code = 'operation' AND t.name IN ('sys.ping', 'agent.count', 'agent.list.query', 'property.list.query', 'agent.detail.query', 'property.detail.query') AND NOT EXISTS (SELECT 1 FROM scope_tool st WHERE st.scope_id = s.id AND st.tool_id = t.id); COMMIT; diff --git a/xiaoqu-agent-service/src/main/java/me/iiv/agent/entity/m0/InvoiceInfo.java b/xiaoqu-agent-service/src/main/java/me/iiv/agent/entity/m0/InvoiceInfo.java new file mode 100644 index 0000000..6fac8b4 --- /dev/null +++ b/xiaoqu-agent-service/src/main/java/me/iiv/agent/entity/m0/InvoiceInfo.java @@ -0,0 +1,141 @@ +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; + +/** + *

+ * 发票信息表 + *

+ * + * @author codegen + * @since 2026-09-08 + */ +@Getter +@Setter +@ToString +@TableName("invoice_info") +public class InvoiceInfo implements Serializable { + + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 创建id + */ + private Long createId; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 创建人 + */ + private String createName; + + /** + * 修改id + */ + private Long modifyId; + + /** + * 修改人 + */ + private String modifyName; + + /** + * 修改时间 + */ + private Date modifyTime; + + /** + * 数据状态 + */ + private Integer softDelete; + + /** + * 类型(0:代理;1:物业) + */ + private Integer type; + + /** + * 客户ID + */ + private Long customerId; + + /** + * 客户名称 + */ + private String customerName; + + /** + * 开票名称 + */ + private String invoiceName; + + /** + * 统一社会信用代码 + */ + private String identificationNumber; + + /** + * 联系人 + */ + private String contact; + + /** + * 联系人邮箱 + */ + private String mail; + + /** + * 银行账户户名称 + */ + private String accountName; + + /** + * 银行账号 + */ + private String accountNo; + + /** + * 开户行名称 + */ + private String accountBank; + + /** + * 开票通讯地址 + */ + private String invoiceAddress; + + /** + * 开票联系电话 + */ + private String invoiceContact; + + /** + * 备注信息 + */ + private String memo; + + /** + * 默认标识(0:非默认;1:默认) + */ + private Integer defaultFlag; + + /** + * 发票抬头类型(0:企业单位;1:个人|非企业) + */ + private Integer invoiceType; +} diff --git a/xiaoqu-agent-service/src/main/java/me/iiv/agent/entity/m0/Properyt.java b/xiaoqu-agent-service/src/main/java/me/iiv/agent/entity/m0/Properyt.java index 7045e40..51bad78 100644 --- a/xiaoqu-agent-service/src/main/java/me/iiv/agent/entity/m0/Properyt.java +++ b/xiaoqu-agent-service/src/main/java/me/iiv/agent/entity/m0/Properyt.java @@ -1,6 +1,7 @@ package me.iiv.agent.entity.m0; import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Getter; import lombok.Setter; @@ -119,7 +120,10 @@ public class Properyt implements Serializable { /** * 客户id自动生成 + * 注意:库中列名为驼峰 customerId(dev/prod 一致),需显式指定, + * 否则 MyBatis-Plus 默认驼峰转下划线会映射成不存在的 customer_id。 */ + @TableField("customerId") private String customerId; /** diff --git a/xiaoqu-agent-service/src/main/java/me/iiv/agent/mapper/m0/InvoiceInfoMapper.java b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mapper/m0/InvoiceInfoMapper.java new file mode 100644 index 0000000..4e8877d --- /dev/null +++ b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mapper/m0/InvoiceInfoMapper.java @@ -0,0 +1,16 @@ +package me.iiv.agent.mapper.m0; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import me.iiv.agent.entity.m0.InvoiceInfo; + +/** + *

+ * 发票信息表 Mapper 接口 + *

+ * + * @author codegen + * @since 2026-09-08 + */ +public interface InvoiceInfoMapper extends BaseMapper { + +} diff --git a/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/AgentTools.java b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/AgentTools.java index 633ec9d..1a55865 100644 --- a/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/AgentTools.java +++ b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/AgentTools.java @@ -2,8 +2,10 @@ package me.iiv.agent.mcp.tool; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import me.iiv.agent.entity.m0.Agent; +import me.iiv.agent.entity.m0.InvoiceInfo; import me.iiv.agent.entity.m0.User; import me.iiv.agent.mapper.m0.AgentMapper; +import me.iiv.agent.mapper.m0.InvoiceInfoMapper; import me.iiv.agent.mcp.ToolObject; import me.iiv.agent.mcp.XqTool; import me.iiv.agent.permission.PermissionService; @@ -31,10 +33,13 @@ public class AgentTools implements ToolObject { private static final int MAX_ROWS = 100; final AgentMapper agentMapper; + final InvoiceInfoMapper invoiceInfoMapper; final PermissionService permissionService; - public AgentTools(AgentMapper agentMapper, PermissionService permissionService) { + public AgentTools(AgentMapper agentMapper, InvoiceInfoMapper invoiceInfoMapper, + PermissionService permissionService) { this.agentMapper = agentMapper; + this.invoiceInfoMapper = invoiceInfoMapper; this.permissionService = permissionService; } @@ -154,4 +159,78 @@ public class AgentTools implements ToolObject { result.put("records", records); return result; } + + /** + * 查询代理商详情(含发票信息)。行权限复用列表精确查询语义: + * 代理商只能查自己或直属下级,总部不限。 + * invoices 来自 m0.invoice_info(type=0 + customer_id=id),含该代理名下全部抬头。 + */ + @XqTool(category = "customer", kind = "read") + @Tool(name = "agent.detail.query", description = "查询代理商详情(含发票信息)。" + + "行级权限:代理商身份的用户(权限组 isagent=1)只能查询自己或直属下级代理;总部身份(isagent=0)可查询任意合法 id。" + + "返回 record(代理商基础信息 + 默认发票镜像字段 + 纳税信息)和 invoices(该代理名下全部发票抬头,defaultFlag=1 为默认抬头,按默认优先+修改时间倒序)。" + + "taxpayerType 含义:0=一般纳税人 1=小规模 2=个人;invoiceType 含义:0=企业单位 1=个人|非企业。" + + "id 不存在或无权限时 record 为 null 并在 notice 字段说明。") + public Map agentDetailQuery( + @ToolParam(required = true, description = "代理商 id;代理商仅限自己或直属下级") Long id) { + User user = AuthContextHolder.get().getUser(); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .eq(Agent::getSoftDelete, 0); + permissionService.applyAgentListRowFilter(wrapper, user, id); + Agent agent = agentMapper.selectOne(wrapper); + + Map result = new LinkedHashMap<>(); + if (agent == null) { + result.put("record", null); + result.put("invoices", List.of()); + result.put("notice", "id=" + id + " 不存在或当前身份无权查看"); + return result; + } + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Map record = new LinkedHashMap<>(); + record.put("id", agent.getId()); + record.put("name", agent.getName()); + record.put("shortName", agent.getShortName()); + record.put("level", agent.getLevel()); + record.put("channelNo", agent.getChannelNo()); + record.put("serialnumber", agent.getSerialnumber()); + record.put("supervisorName", resolveSupervisorName(agent)); + record.put("province", agent.getProvince()); + record.put("city", agent.getCity()); + record.put("countyseat", agent.getCountyseat()); + record.put("address", agent.getAddress()); + record.put("agencyarea", agent.getAgencyarea()); + record.put("contact", agent.getContact()); + record.put("phone", agent.getPhone()); + record.put("mail", agent.getMail()); + record.put("saleName", agent.getSaleName()); + record.put("memo", agent.getMemo()); + record.put("createTime", agent.getCreateTime() == null ? null : sdf.format(agent.getCreateTime())); + // 默认发票镜像字段(与 invoice_info 中 default_flag=1 的抬头同步) + record.put("invoiceName", agent.getInvoiceName()); + record.put("invoiceType", agent.getInvoiceType()); + record.put("identificationNumber", agent.getIdentificationNumber()); + record.put("accountName", agent.getAccountName()); + record.put("accountNo", agent.getAccountNo()); + record.put("accountBank", agent.getAccountBank()); + record.put("invoiceAddress", agent.getInvoiceAddress()); + record.put("invoiceContact", agent.getInvoiceContact()); + record.put("lastInvoiceTime", agent.getLastInvoiceTime() == null ? null : sdf.format(agent.getLastInvoiceTime())); + // 纳税信息(agent 独有) + record.put("taxpayerType", agent.getTaxpayerType()); + record.put("taxpayerPoint", agent.getTaxpayerPoint()); + record.put("taxpayerServiceCharge", agent.getTaxpayerServiceCharge()); + result.put("record", record); + result.put("invoices", InvoiceQueries.query(invoiceInfoMapper, id, 0)); + return result; + } + + private String resolveSupervisorName(Agent agent) { + if (agent.getSupervisorId() == null || agent.getSupervisorId() == 0) { + return "-"; + } + Agent supervisor = agentMapper.selectById(agent.getSupervisorId()); + return supervisor == null ? "-" : supervisor.getName(); + } } diff --git a/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/InvoiceQueries.java b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/InvoiceQueries.java new file mode 100644 index 0000000..b0c3ace --- /dev/null +++ b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/InvoiceQueries.java @@ -0,0 +1,48 @@ +package me.iiv.agent.mcp.tool; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import me.iiv.agent.entity.m0.InvoiceInfo; +import me.iiv.agent.mapper.m0.InvoiceInfoMapper; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * invoice_info 抬头查询助手(m0 库,type:0=代理 1=物业)。 + * 默认条(defaultFlag=1)优先,其余按修改时间倒序。 + */ +final class InvoiceQueries { + + private InvoiceQueries() { + } + + static List> query(InvoiceInfoMapper mapper, Long customerId, int type) { + List invoices = mapper.selectList(new LambdaQueryWrapper() + .eq(InvoiceInfo::getSoftDelete, 0) + .eq(InvoiceInfo::getCustomerId, customerId) + .eq(InvoiceInfo::getType, type) + .orderByDesc(InvoiceInfo::getDefaultFlag) + .orderByDesc(InvoiceInfo::getModifyTime)); + List> rows = new ArrayList<>(); + for (InvoiceInfo invoice : invoices) { + Map row = new LinkedHashMap<>(); + row.put("id", invoice.getId()); + row.put("invoiceName", invoice.getInvoiceName()); + row.put("invoiceType", invoice.getInvoiceType()); + row.put("identificationNumber", invoice.getIdentificationNumber()); + row.put("accountName", invoice.getAccountName()); + row.put("accountNo", invoice.getAccountNo()); + row.put("accountBank", invoice.getAccountBank()); + row.put("invoiceAddress", invoice.getInvoiceAddress()); + row.put("invoiceContact", invoice.getInvoiceContact()); + row.put("contact", invoice.getContact()); + row.put("mail", invoice.getMail()); + row.put("defaultFlag", invoice.getDefaultFlag()); + row.put("memo", invoice.getMemo()); + rows.add(row); + } + return rows; + } +} diff --git a/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/PropertyTools.java b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/PropertyTools.java index 07e9e9d..1b33558 100644 --- a/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/PropertyTools.java +++ b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/PropertyTools.java @@ -5,6 +5,7 @@ import me.iiv.agent.entity.m0.Agent; import me.iiv.agent.entity.m0.Properyt; import me.iiv.agent.entity.m0.User; import me.iiv.agent.mapper.m0.AgentMapper; +import me.iiv.agent.mapper.m0.InvoiceInfoMapper; import me.iiv.agent.mapper.m0.ProperytMapper; import me.iiv.agent.mapper.m0.UserMapper; import me.iiv.agent.mcp.ToolObject; @@ -37,13 +38,16 @@ public class PropertyTools implements ToolObject { final ProperytMapper properytMapper; final AgentMapper agentMapper; final UserMapper userMapper; + final InvoiceInfoMapper invoiceInfoMapper; final PermissionService permissionService; public PropertyTools(ProperytMapper properytMapper, AgentMapper agentMapper, - UserMapper userMapper, PermissionService permissionService) { + UserMapper userMapper, InvoiceInfoMapper invoiceInfoMapper, + PermissionService permissionService) { this.properytMapper = properytMapper; this.agentMapper = agentMapper; this.userMapper = userMapper; + this.invoiceInfoMapper = invoiceInfoMapper; this.permissionService = permissionService; } @@ -149,4 +153,73 @@ public class PropertyTools implements ToolObject { result.put("records", records); return result; } + + /** + * 查询物业详情(含发票信息)。行权限:列表过滤语义 + 精确 id 叠加, + * 代理商/销售只见其有权限的物业,总部不限。 + * invoices 来自 m0.invoice_info(type=1 + customer_id=id),含该物业名下全部抬头。 + */ + @XqTool(category = "customer", kind = "read") + @Tool(name = "property.detail.query", description = "查询物业(客户)详情(含发票信息),数据来源 m0 库 properyt 表。" + + "行级权限与 property.list.query 一致:代理商身份只能查 自己+直属下级代理+被授权代管代理 名下或被授权代管的物业;" + + "销售部门非负责人只能查自己创建的及被授权代管代理名下的物业;总部身份可查任意合法 id。" + + "返回 record(物业基础信息 + 默认发票镜像字段)和 invoices(该物业名下全部发票抬头,defaultFlag=1 为默认抬头,按默认优先+修改时间倒序)。" + + "invoiceType 含义:0=企业单位 1=个人|非企业。id 不存在或无权限时 record 为 null 并在 notice 字段说明。") + public Map propertyDetailQuery( + @ToolParam(required = true, description = "物业 id;仅可查有行级权限的物业") Long id) { + User user = AuthContextHolder.get().getUser(); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .eq(Properyt::getSoftDelete, 0) + .eq(Properyt::getId, id); + permissionService.applyPropertyRowFilter(wrapper, user); + Properyt properyt = properytMapper.selectOne(wrapper); + + Map result = new LinkedHashMap<>(); + if (properyt == null) { + result.put("record", null); + result.put("invoices", List.of()); + result.put("notice", "id=" + id + " 不存在或当前身份无权查看"); + return result; + } + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Map record = new LinkedHashMap<>(); + record.put("id", properyt.getId()); + record.put("name", properyt.getName()); + record.put("serialnumber", properyt.getSerialnumber()); + record.put("agentName", resolveAgentName(properyt)); + record.put("province", properyt.getProvince()); + record.put("city", properyt.getCity()); + record.put("countyseat", properyt.getCountyseat()); + record.put("address", properyt.getAddress()); + record.put("contact", properyt.getContact()); + record.put("phone", properyt.getPhone()); + record.put("mail", properyt.getMail()); + record.put("memo", properyt.getMemo()); + record.put("districtOnCount", properyt.getDistrictOnCount()); + record.put("hardOnCount", properyt.getHardOnCount()); + record.put("userCount", properyt.getUserCount()); + record.put("createTime", properyt.getCreateTime() == null ? null : sdf.format(properyt.getCreateTime())); + // 默认发票镜像字段(与 invoice_info 中 default_flag=1 的抬头同步) + record.put("invoiceName", properyt.getInvoiceName()); + record.put("invoiceType", properyt.getInvoiceType()); + record.put("identificationNumber", properyt.getIdentificationNumber()); + record.put("accountName", properyt.getAccountName()); + record.put("accountNo", properyt.getAccountNo()); + record.put("accountBank", properyt.getAccountBank()); + record.put("invoiceAddress", properyt.getInvoiceAddress()); + record.put("invoiceContact", properyt.getInvoiceContact()); + record.put("lastInvoiceTime", properyt.getLastInvoiceTime() == null ? null : sdf.format(properyt.getLastInvoiceTime())); + result.put("record", record); + result.put("invoices", InvoiceQueries.query(invoiceInfoMapper, id, 1)); + return result; + } + + private String resolveAgentName(Properyt properyt) { + if (properyt.getAgentId() == null) { + return "未查询到"; + } + Agent agent = agentMapper.selectById(properyt.getAgentId()); + return agent == null ? "未查询到" : agent.getName(); + } } diff --git a/xiaoqu-agent-service/src/test/java/me/iiv/agent/codegen/CodeGenerator.java b/xiaoqu-agent-service/src/test/java/me/iiv/agent/codegen/CodeGenerator.java index 0c27cab..fbfd83b 100644 --- a/xiaoqu-agent-service/src/test/java/me/iiv/agent/codegen/CodeGenerator.java +++ b/xiaoqu-agent-service/src/test/java/me/iiv/agent/codegen/CodeGenerator.java @@ -27,6 +27,8 @@ import java.util.Arrays; * CODEGEN_SCHEMA_M0 默认 xiaoqu_comples_d(生产 xiaoqu_complex) * CODEGEN_SCHEMA_M2 默认 xiaoqu_franchisee_d(生产 xiaoqu_franchisee) * CODEGEN_SCHEMA_SVC 默认 xiaoqu_agent_svc_d(svc 表结构由 Flyway 管理,各环境一致,一般无需覆盖) + * 注意:properyt.customerId 是驼峰列名(dev/prod 一致),生成后必须保留实体上的 + * @TableField("customerId") 手工补丁,否则运行时会映射成不存在的 customer_id。 */ public class CodeGenerator { @@ -38,7 +40,7 @@ public class CodeGenerator { public static void main(String[] args) { generate(envOr("CODEGEN_SCHEMA_M0", "xiaoqu_comples_d"), "m0", - "user", "agent", "authority", "properyt", "agent_manage_district_auth"); + "user", "agent", "authority", "properyt", "agent_manage_district_auth", "invoice_info"); generate(envOr("CODEGEN_SCHEMA_M2", "xiaoqu_franchisee_d"), "m2", "warehouse_agent_auth"); generate(envOr("CODEGEN_SCHEMA_SVC", "xiaoqu_agent_svc_d"), "svc", diff --git a/xiaoqu-agent-service/src/test/java/me/iiv/agent/permission/PermissionServiceTest.java b/xiaoqu-agent-service/src/test/java/me/iiv/agent/permission/PermissionServiceTest.java index 09b2fae..d8c6ea6 100644 --- a/xiaoqu-agent-service/src/test/java/me/iiv/agent/permission/PermissionServiceTest.java +++ b/xiaoqu-agent-service/src/test/java/me/iiv/agent/permission/PermissionServiceTest.java @@ -200,6 +200,37 @@ class PermissionServiceTest { assertTrue(wrapper.getParamNameValuePairs().values().contains(55L)); } + @Test + void propertyDetailKeepsExactIdAndAgentBoundary() { + // property.detail.query 的过滤形态:eq(id) 精确查询 + applyPropertyRowFilter 行权限 + User user = user(1000L, 108L, 1L); + when(authorityMapper.selectById(108L)).thenReturn(authority(108L, 1)); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .eq(Properyt::getId, 55L); + permissionService.applyPropertyRowFilter(wrapper, user); + + String sql = wrapper.getSqlSegment(); + Collection values = wrapper.getParamNameValuePairs().values(); + assertTrue(sql.contains("id =")); + assertTrue(sql.contains("agent_id")); + assertTrue(values.contains(55L)); + assertTrue(values.contains(1L)); + } + + @Test + void headquartersPropertyDetailKeepsOnlyExactId() { + User user = user(46L, 1L, 0L); + when(authorityMapper.selectById(1L)).thenReturn(authority(1L, 0)); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .eq(Properyt::getId, 55L); + permissionService.applyPropertyRowFilter(wrapper, user); + + assertTrue(wrapper.getSqlSegment().contains("id =")); + assertFalse(wrapper.getSqlSegment().contains("agent_id")); + } + private static Authority authority(Long id, int isAgent) { Authority authority = new Authority(); authority.setId(id);