hugray-user-miniapp(pages/profile/edit.vue 入口、新增 pages/profile/certification.vue)hugray-admin/src/views/member/certification/index.vue(主体认证审核列表,菜单 2202)doc/design/用户端_我的资料_头像上传_20261013.md(同类 C 端写接口范式)、doc/design/用户端_我的资料_20261013.html(本页 UI 参考)192.168.110.111:3306/hugray-account(实际表结构优先于本文档)「我的资料」页(pages/profile/edit.vue)「实名认证」行当前点击仅弹占位 toast,本次改为**跳转到实名认证页**:
pages/profile/certification.vue(自定义导航栏 + 姓名 / 身份证号两个输入 + 底部「提交」按钮,见原型图);account_certification_apply(subject_type='1' 个人、audit_status='0' 待审核),并联动 account_user.auth_status='1'(审核中),使其出现在后台**主体认证审核列表**;authStatus 显示真实状态(未认证 / 认证中 / 已认证 / 认证失败),形成闭环。后台「审」链路(列表 / 详情 / 通过 / 驳回)**已存在且无需改动**:AccountCertificationApplyController(/account/user/certification/**,权限 member:certification:*)、AccountCertificationApplyServiceImpl(审核 + auth_status 联动 + 通过后落 account_certification)。本次只补 C 端"提交"这一缺口。
AccountCertificationApplyController 类注释明确:*"本模块只有"审"没有"增删":申请由 C 端会员提交(不在本仓库范围)"*。C 端提交接口与页面均需**新建**。
| 表 | 用途 | 关键列 |
|---|---|---|
account_certification_apply |
每次提交的申请(驳回重提多一条),**本次落这张表** | id(自增主键兼申请号)、user_id、subject_type、real_name、id_card、audit_status、del_flag、create_by、create_time |
account_certification |
当前生效认证资料,审核**通过**时由服务层落一份 | 审核通过后写入,本次不直接写 |
account_user.auth_status |
会员整体认证状态 | 0未认证 1审核中 2已认证 3认证失败 |
状态联动口径(见 DDL 注释):**提交申请 →
auth_status='1';通过 →'2';驳回 →'3'**。通过/驳回联动已由既有审核服务实现,本次只补"提交 → '1'"。
account_certification_apply.id 为 bigint AUTO_INCREMENT(既有表,非雪花),故 insert 走 useGeneratedKeys="true" keyProperty="id",与既有 insertCertification 一致。
状态/类型取值语义已在多处存在,本次沿用既有字符串约定(与 AccountCertificationApplyServiceImpl 中的 AUDIT_PASS="1" 等常量同风格),在服务层抽为常量,不散落魔法值。
user_id;SecurityUtils.getUserId() 取会员身份,**C 端接口不接受前端传 userId**(防水平越权)。AccountProfileController(@RequestMapping("/user"),外部 /miniapp/account/user/**)已承载 /info、/avatar,本次**在其上新增** POST /user/certification(外部 POST /miniapp/account/user/certification)。AccountCertificationApplyController(外部 /account/user/certification/**)**前缀不同域**(/miniapp/account vs /account),无冲突。统一响应体:C 端 R<T>(成功 code=200);后台 AjaxResult / TableDataInfo。
POST /miniapp/account/user/certification(网关 StripPrefix=2 → 服务内 POST /user/certification)AuthFilter 直接返回 401,前端 request.js 自动跳登录页;**后端不做未登录分支**。user_id 头,**不接受前端传 userId**| 字段 | 类型 | 必填 | 校验 | 说明 |
|---|---|---|---|---|
| realName | String | 是 | @NotBlank + @Size(max=64) |
姓名(对应 real_name varchar(64)) |
| idCard | String | 是 | @NotBlank + 18 位格式 @Pattern + @Size(max=32) |
身份证号(对应 id_card varchar(32)) |
R<Void>(成功即 code=200)姓名不能为空 / 姓名过长 / 身份证号不能为空 / 身份证号格式不正确 / 已有实名认证申请正在审核中,请勿重复提交身份证号基础格式正则(18 位:地区码 6 位 + 出生日期 + 顺序码 + 校验位;**只校验结构与日期范围,不校验校验码、不核验真实性**):
^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$
GET /miniapp/account/user/info(既有)AccountUserInfoVo 新增 authStatus 字段(account_user.auth_status 原值:0/1/2/3),供「我的资料」页显示实名认证真实状态。手机号仍只下发脱敏值。GET /account/user/certification/list(member:certification:list)、GET /account/user/certification/detail/{id}、PUT /account/user/certification/audit(member:certification:audit)。C 端提交的申请将直接出现在该列表(subject_type='1' 个人、audit_status='0' 待审核,列表按待审核在前排序)。
库:hugray-account。
无 DDL 变更:account_certification_apply 与 account_user.auth_status 均已存在,本次仅插入 / 更新。
pages/profile/certification.vue)STEP 1: onShow 校验登录态(ensureLogin),未登录跳登录页
STEP 2: 用户填写姓名 / 身份证号
STEP 3: 点「提交」→ 本地校验:姓名非空(trim)且 ≤64;身份证号匹配 18 位正则
STEP 4: 校验不通过则 toast 提示并中止,不发请求
STEP 5: 调 submitCertification(realName, idCard) → POST /miniapp/account/user/certification
STEP 6: 成功后 toast「提交成功,等待审核」并 navigateBack 回我的资料页;失败提示后端错误信息
AccountCertificationApplyServiceImpl.submitPersonalCertification)STEP 1: 校验 userId 非空(未登录由网关拦截,此处兜底),为空抛业务异常
STEP 2: 防重复提交:该会员若存在 audit_status='0'(待审核)且未删除的申请,
抛「已有实名认证申请正在审核中,请勿重复提交」
STEP 3: 组装申请快照:subject_type='1'(个人)、real_name、id_card、
audit_status='0'、create_by=登录用户名
STEP 4: insertCertificationApply 落 account_certification_apply(自增回填 id)
STEP 5: 联动 account_user.auth_status='1'(审核中,调既有 updateUserAuthStatus 单列语句)
STEP 6: 以上 STEP 4/5 同事务(@Transactional),任一失败整体回滚
C 端提交 ──► account_certification_apply.audit_status='0' + account_user.auth_status='1'
│
后台「通过」──► audit_status='1' + auth_status='2' + 落 account_certification(既有逻辑)
后台「驳回」──► audit_status='2' + auth_status='3' + 记审核意见(既有逻辑)
| 名称 | 取值 | 说明 |
|---|---|---|
subject_type 主体类型 |
1 个人 / 2 个体工商户 / 3 企业 |
本次固定提交 1(个人实名);企业认证入口本期仍为占位 |
audit_status 审核状态 |
0 待审核 / 1 已通过 / 2 已驳回 |
提交置 0,审核改 1/2 |
auth_status 会员认证状态 |
0 未认证 / 1 审核中 / 2 已认证 / 3 认证失败 |
提交置 1;通过置 2;驳回置 3 |
「我的资料」页徽标与 auth_status 映射:0→未认证、1→认证中、2→已认证、3→认证失败。
| 文件 | 说明 |
|---|---|
domain/dto/AccountCertificationApplyDto.java |
C 端实名认证入参(realName / idCard,含校验注解与字段注释) |
src/test/.../AccountCertificationApplyServiceImplTest.java |
提交服务单测(正常 / 待审核重复提交拒绝 / 状态联动) |
| 文件 | 改动 |
|---|---|
controller/AccountProfileController.java |
注入 IAccountCertificationApplyService;新增 POST /user/certification |
service/IAccountCertificationApplyService.java |
新增 submitPersonalCertification(Long userId, String realName, String idCard) |
service/impl/AccountCertificationApplyServiceImpl.java |
实现提交(防重校验 + 落申请 + 联动 auth_status='1',@Transactional) |
mapper/AccountCertificationApplyMapper.java |
新增 insertCertificationApply(AccountCertificationApply)、countPendingApplyByUserId(Long userId) |
resources/mapper/user/AccountCertificationApplyMapper.xml |
新增上述两条 SQL |
domain/vo/AccountUserInfoVo.java |
新增 authStatus 字段 |
service/impl/AccountUserServiceImpl.java |
getAccountUserInfo 填充 authStatus |
src/test/.../AccountUserServiceImplTest.java |
补充 authStatus 下发断言 |
| 文件 | 说明 |
|---|---|
pages/profile/certification.vue |
实名认证页(自定义导航栏 + 姓名/身份证号输入 + 底部提交;沿用 edit.vue 的导航与按钮风格) |
| 文件 | 改动 |
|---|---|
pages.json |
注册 pages/profile/certification(navigationStyle: custom) |
api/index.js |
新增 submitCertification(realName, idCard)(→ /miniapp/account/user/certification) |
pages/profile/edit.vue |
「实名认证」行由 handlePlaceholder 改为 uni.navigateTo 跳认证页;realNameStatus 改为按 /info 返回的 authStatus 映射 |
网关无需改动:
/miniapp/account/**路由已存在。
| # | 场景 | 处理 |
|---|---|---|
| 1 | 未登录访问 | 网关 AuthFilter 返回 401,前端跳登录页;后端不做分支 |
| 2 | user_id 头缺失/为空 |
服务内兜底判空,抛业务异常(不 NPE) |
| 3 | 姓名为空 / 纯空格 | DTO @NotBlank 拦截,提示「姓名不能为空」 |
| 4 | 姓名超长(>64) | DTO @Size 拦截,提示「姓名过长」 |
| 5 | 身份证号为空 | DTO @NotBlank 拦截,提示「身份证号不能为空」 |
| 6 | 身份证号格式非法(非 18 位 / 含非法字符 / 非 X 结尾) | DTO @Pattern 拦截,提示「身份证号格式不正确」 |
| 7 | 已有待审核申请再次提交 | 服务层 countPendingApplyByUserId>0 时抛「已有实名认证申请正在审核中,请勿重复提交」 |
| 8 | 落库或状态联动失败 | 同一事务回滚,返回统一错误,不产生半更新 |
| 9 | 越权 | 会员身份取自令牌注入头,不接受前端传 userId,天然防水平越权 |
| 10 | 三方实名核验 | 本期明确不做,仅基础格式校验 |
submitPersonalCertification / insertCertificationApply / countPendingApplyByUserId 见名知意;DTO 与 Entity 分离。// STEP x: 分步注释;实体状态字段注明取值。subject_type、audit_status、auth_status 取值抽为常量;错误提示集中。insert + 联动更新 同事务,保证一致性。SELECT *;无 ${} 拼接;无循环查库。AccountUserInfoVo.authStatus,「我的资料」页徽标显示真实状态(未认证/认证中/已认证/认证失败)。subject_type='1');「企业/个体户认证」入口维持占位。进入实名认证页时,按当前认证状态**回填**该会员已提交的资料,并区分交互:
| 当前状态 | 姓名 / 身份证号 | 底部按钮(文案 / 可点性) | 驳回原因 |
|---|---|---|---|
| 未认证(无申请) | 空,可编辑 | 「提交」/ 可点 | 不展示 |
| 认证中(待审核) | 回填,**只读** | 「审核中」/ 置灰不可点 | 不展示 |
| 认证成功(已通过) | 回填,**只读** | 「已认证」/ 置灰不可点 | 不展示 |
| 认证失败(已驳回) | 回填,**可编辑** | 「重新提交」/ 可点 | 在身份证号下方展示 |
已确认决策:认证成功同样置灰不可点(已认证无需重复提交);认证中 / 认证成功输入框只读不可编辑。
GET /miniapp/account/user/certification(网关 StripPrefix=2 → 服务内 GET /user/certification)POST /miniapp/account/user/certification(提交)**同路径不同方法**,无需新增路由。取该会员**最近一次**申请(account_certification_apply 按 id desc limit 1,只取 del_flag='0'),出参 AccountCertificationStatusVo:
| 字段 | 类型 | 说明 |
|---|---|---|
| realName | String | 姓名(回填用) |
| idCard | String | 身份证号(回填用) |
| auditStatus | String | 0待审核 1已通过 2已驳回;**为空表示从未提交** |
| auditRemark | String | 驳回原因(audit_status='2' 时有值) |
无申请时返回**空字段对象**(各字段为 null),前端按「未认证」处理,不返回 404。
auditStatus → 页面状态:'0'→认证中、'1'→认证成功、'2'→认证失败、空/缺省→未认证。
auditRemark 非空;新增(ruoyi-user)
| 文件 | 说明 |
|---|---|
domain/vo/AccountCertificationStatusVo.java |
C 端认证状态回填出参(realName / idCard / auditStatus / auditRemark) |
修改(ruoyi-user)
| 文件 | 改动 |
|---|---|
mapper/AccountCertificationApplyMapper.java |
新增 selectLatestApplyByUserId(Long userId) |
resources/mapper/user/AccountCertificationApplyMapper.xml |
新增上述查询 SQL(取最近一条) |
service/IAccountCertificationApplyService.java |
新增 getCertificationStatus(Long userId) |
service/impl/AccountCertificationApplyServiceImpl.java |
实现查询:取最近一条申请映射为 VO,无则返回空对象 |
controller/AccountProfileController.java |
新增 GET /user/certification |
src/test/.../AccountCertificationApplyServiceImplTest.java |
补充查询用例(有申请回填 / 无申请空对象) |
修改(前端 hugray-user-miniapp)
| 文件 | 改动 |
|---|---|
api/index.js |
新增 getCertificationStatus()(→ GET /miniapp/account/user/certification) |
pages/profile/certification.vue |
进入页面拉取状态并回填;认证中/成功输入框只读、按钮置灰且文案随状态变化(提交/审核中/已认证/重新提交);认证失败展示驳回原因 |
无需 DDL、无需网关改动(
/miniapp/account/**路由已存在)。
| # | 场景 | 处理 |
|---|---|---|
| 1 | 从未提交(查询返回空对象) | 页面按未认证处理,空表单可编辑可提交 |
| 2 | 查询接口异常 | 静默降级为未认证(空表单),不阻塞页面 |
| 3 | 认证中 / 认证成功 | 输入框 disabled 只读,按钮置灰且点击直接 return |
| 4 | 认证失败但驳回原因为空 | 不展示驳回原因区域(仅可编辑重提) |
| 5 | 认证失败后重新提交成功 | 回退上一页;再次进入时按新的「待审核」状态回填只读 |