spec.md 3.9 KB

ADDED Requirements

Requirement: 集成平台 OAuth2 鉴权与 token 缓存

INTPImplClient_User#getAccessToken() SHALL 通过 client_credentials 流程获取 access_token,并使用 UtilToken 缓存。CRUD 方法 MUST 接收 access_token 作为第一个参数,调用方先调 getAccessToken() 再传入。

Scenario: 首次拉取 access_token

  • WHEN UtilToken 缓存中无 integration:accessToken
  • THEN{baseUrl}/iam/token 发 POST,body 为 application/x-www-form-urlencoded,字段:grant_type=client_credentials / client_id={IntpConf.clientId} / client_secret={IntpConf.clientSecret}
  • AND 响应 access_token 写入缓存,TTL = expires_in - 60

Scenario: 缓存命中

  • WHEN UtilToken 缓存中已有未过期的 integration:accessToken
  • THEN 直接复用,不发 HTTP 请求

Scenario: token 失效

  • WHEN 服务端 revoke 或过期
  • THEN 调用方收到 HTTP 401 + error/error_description
  • AND 本期不做"401 自动重拿",由调用方决定重试策略

Requirement: 用户创建

INTPClient_User.createUser SHALL 对齐 apifox 接口 POST /iam/api/users。必填字段必须显式入参;可选字段通过 body_ext Map 注入。

Scenario: 创建用户(最小集)

  • WHEN 调用 createUser(accessToken, "alice", "Pwd@123", null)
  • THEN{baseUrl}/iam/api/users 发 POST application/json
  • AND Header Authorization: Bearer {accessToken}
  • AND body 为 {"username":"alice","password":"Pwd@123"}

Scenario: 创建用户(完整字段)

  • WHEN body_extname / email / phone_number / user_job_number / nick_name / picture / org_ids / address / title / hired_date / tag / group_positions
  • THEN body 合并 username / passwordbody_ext 全部字段
  • AND javadoc MUST 枚举上述字段名 + 类型 + 含义

Scenario: 创建失败

  • WHEN 服务端返回 {"result":false,"error":"USER_EXIST","error_description":"用户已存在"}
  • THEN 返回 McR.error("USER_EXIST", "用户已存在")

Requirement: 用户修改

INTPClient_User.updateUser SHALL 对齐 apifox 接口 PATCH /iam/api/userusername 通过 Query Param 传递(非 Path),其它字段全部走 body。

Scenario: 修改单字段

  • WHEN 调用 updateUser(accessToken, "alice", Map.of("email", "alice@new.com"))
  • THEN{baseUrl}/iam/api/user?username=alice 发 PATCH
  • AND body 为 {"email":"alice@new.com"}
  • AND 不传的字段保持服务端原值不变

Requirement: 用户删除(批量)

INTPClient_User.deleteUsers SHALL 对齐 apifox 接口 POST /iam/api/users/deleteHTTP 方法是 POST,因 body 需带 username 数组。

Scenario: 批量按登录名删除

  • WHEN 调用 deleteUsers(accessToken, List.of("alice","bob"))
  • THEN{baseUrl}/iam/api/users/delete 发 POST application/json
  • AND body 为 {"usernames":["alice","bob"]}

Requirement: 用户查询

INTPClient_User.queryUsers SHALL 对齐 apifox 接口 GET /iam/api/users,支持分页与多维过滤。

Scenario: 关键字搜索

  • WHEN 调用 queryUsers(accessToken, Map.of("q","alice","page",1,"size",20))
  • THEN{baseUrl}/iam/api/users?q=alice&page=1&size=20 发 GET
  • AND 解析响应 data.items[] 返回 McR.success(data)

Requirement: 配置段独立命名

INTPConf SHALL 通过 Spring @ConfigurationProperties("integration") 加载配置;baseUrl 不含 /iam 前缀。

Scenario: 三个客户子模块的 yml 模板

  • WHEN 浏览 mjava-mcli / mjava-shunfeng / mjava-guangmingapplication-{dev,prod}.yml.example
  • THEN 每个模板 MUST 含 integration: 段示例,字段:baseUrl / clientId / clientSecret
  • AND 真实凭据从 ${INTP_CLIENT_ID} / ${INTP_CLIENT_SECRET} 环境变量注入