docs(spec): 新增按企业查询用户接口设计文档

This commit is contained in:
liulujian 2026-07-27 12:02:24 +08:00
parent 4cb79987d3
commit 7ae24236df

View File

@ -0,0 +1,236 @@
# 按企业查询用户接口设计
- 日期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_uuid`、`dlzh`(登录账号)、`yxbz`(有效标志 Y/N、`sdbz`(锁定标志 Y/N |
| `txw_mhzc_qyxxb`(企业表) | `qyuuid`、`qymc`(企业名称) |
| `txw_mhzc_yhqygxb`(用户-企业关系表) | `yh_uuid``qyuuid`**一对多** |
用户过滤硬性条件:`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` |
`dlzh``qymc` 同时为空时,返回所有有效用户-企业匹配项,按 `lrrq DESC` 排序;只填一个时仅按该字段过滤。
### 3.2 响应
```json
{
"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` | 入参 VO`dlzh`、`qymc`,继承 `PageParam` |
| `pojo/vo/UserQyVO.java` | 出参 VO`dlzh` + `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 设计
```xml
<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`
```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`
```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` 新增方法
```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` 新增方法
```java
Page<UserQyVO> listUserByQy(UserQyReqVO reqVO);
```
### 6.5 `TxwMhzcYhxxbServiceImpl.java` 新增实现
```java
@Override
public Page<UserQyVO> listUserByQy(UserQyReqVO reqVO) {
return yhxxbMapper.listUserByQy(reqVO);
}
```
### 6.6 `UserController.java` 新增方法
```java
@PostMapping("/listUserByQy")
@Operation(summary = "按企业查询用户",
description = "按登录账号与企业名称模糊查询用户-企业匹配项")
public CommonResult<Page<UserQyVO>> listUserByQy(@RequestBody UserQyReqVO reqVO) {
return CommonResult.success(yhxxbService.listUserByQy(reqVO));
}
```
## 7. 错误处理
| 场景 | 行为 |
|---|---|
| `dlzh``qymc` 都为空 | 返回所有有效用户-企业项,按 `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=10`、`pageNo` 超过总页数 → 返回空 recordstotal 仍正确
## 9. 不在范围内
- 不修改现有 `getAllUser` 接口及 `UserVO`
- 不新增权限校验(与 `getAllUser` 一致,无显式权限控制,依赖网关层)
- 不缓存查询结果(用户与企业关联可能变化,首次上线不强加缓存策略)
- 不支持按其他字段(手机号、身份证号、纳税人识别号)筛选