txw/docs/superpowers/specs/2026-07-27-user-list-by-qy-design.md

7.2 KiB
Raw Blame History

按企业查询用户接口设计

  • 日期2026-07-27
  • 模块:txw-mhzc(门户主站)
  • 状态:待用户审阅

1. 背景与目标

1.1 需求

新增一个按企业名称(及登录账号)查询用户的接口:

  • 入参:dlzh(登录账号,可空)、qymc(企业名称,可空),均为模糊匹配
  • 出参:符合条件的 dlzh + qymc 列表,分页返回
  • 一个用户可关联多个企业,匹配结果每个企业一条记录dlzh 可重复)

1.2 设计目标

  • 与现有代码风格一致XML 处理复杂 SQL、default 方法处理简单 wrapper
  • 不污染现有 getAllUser 接口,独立新增
  • 单条 SQL 完成三表 JOIN分页由 MyBatis-Plus 插件自动包装

2. 数据模型

关键字段
txw_mhzc_yhxxb(用户表) yh_uuiddlzh(登录账号)、yxbz(有效标志 Y/Nsdbz(锁定标志 Y/N
txw_mhzc_qyxxb(企业表) qyuuidqymc(企业名称)
txw_mhzc_yhqygxb(用户-企业关系表) yh_uuidqyuuid一对多

用户过滤硬性条件:yxbz = 'Y' AND sdbz = 'N'(与 getYhxxByDlzh 等现有查询保持一致)。

3. 接口契约

3.1 请求

POST /user/listUserByQy
Content-Type: application/json

请求体 UserQyReqVO

字段 类型 必填 说明
dlzh String 登录账号模糊匹配关键字
qymc String 企业名称模糊匹配关键字
pageNo Integer 页码(继承 PageParam
pageSize Integer 每页大小(继承 PageParam

dlzhqymc 同时为空时,返回所有有效用户-企业匹配项,按 lrrq DESC 排序;只填一个时仅按该字段过滤。

3.2 响应

{
  "code": 0,
  "msg": "ok",
  "data": {
    "total": 42,
    "current": 1,
    "size": 10,
    "pages": 5,
    "records": [
      { "dlzh": "zhangsan@qy", "qymc": "某某科技有限公司" },
      { "dlzh": "zhangsan@qy", "qymc": "某某集团" }
    ]
  }
}

UserQyVO 字段:

字段 类型 说明
dlzh String 登录账号
qymc String 企业名称

4. 组件设计

4.1 新增文件

文件 作用
pojo/vo/UserQyReqVO.java 入参 VOdlzhqymc,继承 PageParam
pojo/vo/UserQyVO.java 出参 VOdlzh + qymc
mapper/TxwMhzcYhxxbMapper.java 新增 listUserByQy 方法
mapper/TxwMhzcYhxxbMapper.xml 新增对应 <select>
service/TxwMhzcYhxxbService.java 新增接口方法
service/impl/TxwMhzcYhxxbServiceImpl.java 新增实现
controller/UserController.java 新增 controller 方法

4.2 调用链

Controller#listUserByQy
    → Service#listUserByQy
    → Mapper#listUserByQyXML <select>
    → DB: 三表 JOIN + LIKE + 分页
    → Page<UserQyVO>

5. SQL 设计

<select id="listUserByQy"
        resultType="com.css.txw.mhzc.pojo.vo.UserQyVO">
    SELECT y.dlzh AS dlzh, q.qymc AS qymc
    FROM txw_mhzc_yhxxb y
    JOIN txw_mhzc_yhqygxb g ON g.yh_uuid = y.yh_uuid
    JOIN txw_mhzc_qyxxb   q ON q.qyuuid  = g.qyuuid
    WHERE y.yxbz = 'Y' AND y.sdbz = 'N'
      <if test="dlzh != null and dlzh != ''">
        AND y.dlzh LIKE CONCAT('%', #{dlzh}, '%')
      </if>
      <if test="qymc != null and qymc != ''">
        AND q.qymc LIKE CONCAT('%', #{qymc}, '%')
      </if>
    ORDER BY y.lrrq DESC
</select>
  • LIKE 参数通过 MyBatis #{} 占位符绑定,%CONCAT 在 SQL 内拼接,不存在 SQL 注入风险
  • 分页由 MyBatis-Plus 的 PaginationInnerInterceptor 自动包装(项目已有配置)

6. 代码骨架

6.1 UserQyReqVO.java

package com.css.txw.mhzc.pojo.vo;

import com.css.ggzc.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

@Schema(description = "按企业查询用户请求")
@Data
public class UserQyReqVO extends PageParam {

    @Schema(description = "登录账号(模糊匹配,可空)")
    private String dlzh;

    @Schema(description = "企业名称(模糊匹配,可空)")
    private String qymc;
}

6.2 UserQyVO.java

package com.css.txw.mhzc.pojo.vo;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

@Schema(description = "用户-企业匹配结果")
@Data
public class UserQyVO {

    @Schema(description = "登录账号")
    private String dlzh;

    @Schema(description = "企业名称")
    private String qymc;
}

6.3 TxwMhzcYhxxbMapper.java 新增方法

default Page<UserQyVO> listUserByQy(UserQyReqVO reqVO){
    Page<UserQyVO> page = new Page<>(reqVO.getPageNo(), reqVO.getPageSize());
    return selectPage(page, Wrappers.emptyWrapper()
            // 由 XML 中的 <select> 接管
    );
}

备注:项目内 MyBatis-Plus 习惯 default 方法直接用 QueryWrapper,复杂 SQL 才走 XML。本接口需三表 JOIN + LIKE + 分页,直接走 XML。Mapper 接口声明形如 Page<UserQyVO> listUserByQy(UserQyReqVO reqVO),由 XML 中 <select id="listUserByQy"> 提供实现。

6.4 TxwMhzcYhxxbService.java 新增方法

Page<UserQyVO> listUserByQy(UserQyReqVO reqVO);

6.5 TxwMhzcYhxxbServiceImpl.java 新增实现

@Override
public Page<UserQyVO> listUserByQy(UserQyReqVO reqVO) {
    return yhxxbMapper.listUserByQy(reqVO);
}

6.6 UserController.java 新增方法

@PostMapping("/listUserByQy")
@Operation(summary = "按企业查询用户",
           description = "按登录账号与企业名称模糊查询用户-企业匹配项")
public CommonResult<Page<UserQyVO>> listUserByQy(@RequestBody UserQyReqVO reqVO) {
    return CommonResult.success(yhxxbService.listUserByQy(reqVO));
}

7. 错误处理

场景 行为
dlzhqymc 都为空 返回所有有效用户-企业项,按 lrrq DESC
无匹配项 Page{records=[], total=0}HTTP 200不抛业务异常
SQL 异常 由全局 @RestControllerAdvice 兜底返回 CommonResult.error
输入含 SQL 通配符(%_ #{} 预编译参数,不会被识别为通配符,安全

8. 测试

与现有项目风格一致,以手动验证为主:

  1. 空入参{dlzh:"",qymc:"",pageNo:1,pageSize:10} → 返回所有有效用户-企业项
  2. 只按 dlzh:匹配 dlzh 含子串的所有项
  3. 只按 qymc:匹配 qymc 含子串的所有项
  4. 同时填两个AND 关系
  5. 多企业用户:同一 dlzh 出现多条,每条对应一个 qymc
  6. 过滤被锁定用户sdbz='Y' 的用户不出现在结果中
  7. 过滤无效用户yxbz='N' 的用户不出现在结果中
  8. 特殊字符:输入 100% 应作为字面量参与匹配,不作为 SQL 通配符
  9. 分页边界pageSize=10pageNo 超过总页数 → 返回空 recordstotal 仍正确

9. 不在范围内

  • 不修改现有 getAllUser 接口及 UserVO
  • 不新增权限校验(与 getAllUser 一致,无显式权限控制,依赖网关层)
  • 不缓存查询结果(用户与企业关联可能变化,首次上线不强加缓存策略)
  • 不支持按其他字段(手机号、身份证号、纳税人识别号)筛选