From 8ea6a7dd1fcdbd4e22c564ed63a5a8023510161a Mon Sep 17 00:00:00 2001
From: zjk <852185829@qq.com>
Date: 星期三, 14 十月 2026 08:56:45 +0800
Subject: [PATCH] feat: 新增实名认证、企业认证、我的邀请与头像上传功能

---
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountCertificationApplyServiceImpl.java |  246 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 243 insertions(+), 3 deletions(-)

diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountCertificationApplyServiceImpl.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountCertificationApplyServiceImpl.java
index ed932dd..379c627 100644
--- a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountCertificationApplyServiceImpl.java
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountCertificationApplyServiceImpl.java
@@ -10,8 +10,11 @@
 import com.ruoyi.user.domain.AccountCertification;
 import com.ruoyi.user.domain.AccountCertificationApply;
 import com.ruoyi.user.domain.AccountUser;
+import com.ruoyi.user.domain.dto.CompanyCertificationApplyDto;
 import com.ruoyi.user.domain.vo.AccountCertificationApplyDetailVo;
 import com.ruoyi.user.domain.vo.AccountCertificationApplyVo;
+import com.ruoyi.user.domain.vo.AccountCertificationStatusVo;
+import com.ruoyi.user.domain.vo.CompanyCertificationStatusVo;
 import com.ruoyi.user.mapper.AccountCertificationApplyMapper;
 import com.ruoyi.user.mapper.AccountCertificationMapper;
 import com.ruoyi.user.mapper.AccountUserMapper;
@@ -31,11 +34,32 @@
     /** 审核结果:驳回 */
     private static final String AUDIT_REJECT = "2";
 
+    /** 审核状态:待审核(提交后的初始状态) */
+    private static final String AUDIT_PENDING = "0";
+
+    /** account_user.auth_status:审核中 */
+    private static final String AUTH_AUDITING = "1";
+
     /** account_user.auth_status:已认证 */
     private static final String AUTH_VERIFIED = "2";
 
     /** account_user.auth_status:认证失败 */
     private static final String AUTH_FAILED = "3";
+
+    /** 主体类型:个人(C端实名认证固定为该类型) */
+    private static final String SUBJECT_PERSONAL = "1";
+
+    /** 主体类型:个体工商户 */
+    private static final String SUBJECT_INDIVIDUAL = "2";
+
+    /** 主体类型:企业 */
+    private static final String SUBJECT_COMPANY = "3";
+
+    /** 有效期类型:长期(此时起止日期可空) */
+    private static final String VALID_TYPE_LONG_TERM = "1";
+
+    /** 有效期类型:短期(此时起止日期必填) */
+    private static final String VALID_TYPE_SHORT_TERM = "2";
 
     @Autowired
     private AccountCertificationApplyMapper certificationApplyMapper;
@@ -102,18 +126,221 @@
             throw new ServiceException("该申请已审核,不能重复处理");
         }
 
-        // 同步到 account_user.auth_status:会员列表的「认证状态」列取的就是它
+        // 同步认证状态到 account_user:个人写 auth_status,企业/个体写 company_auth_status,两列互不覆盖。
         AccountUser user = new AccountUser();
         user.setUserId(apply.getUserId());
-        user.setAuthStatus(pass ? AUTH_VERIFIED : AUTH_FAILED);
         user.setUpdateBy(operator);
-        accountUserMapper.updateUserAuthStatus(user);
+        if (isCompanySubject(apply.getSubjectType()))
+        {
+            user.setCompanyAuthStatus(pass ? AUTH_VERIFIED : AUTH_FAILED);
+            // 审核通过时才把主体类型落为 2/3;驳回不动,避免未通过审核就被标成企业主体
+            if (pass)
+            {
+                user.setSubjectType(apply.getSubjectType());
+            }
+            accountUserMapper.updateUserCompanyAuthStatus(user);
+        }
+        else
+        {
+            user.setAuthStatus(pass ? AUTH_VERIFIED : AUTH_FAILED);
+            accountUserMapper.updateUserAuthStatus(user);
+        }
 
         if (pass)
         {
             saveCertificationSnapshot(apply, operator);
         }
         return 1;
+    }
+
+    @Override
+    @Transactional
+    public void submitPersonalCertification(Long userId, String realName, String idCard)
+    {
+        // STEP 1: 校验会员ID(登录态缺失时直接失败,避免无归属写入)
+        if (StringUtils.isNull(userId))
+        {
+            throw new ServiceException("提交实名认证失败");
+        }
+        // STEP 2: 姓名/身份证号二次校验:Controller 已用 @Validated 拦过一遍,
+        //         这里是服务层兜底,防止绕过 Controller 直接调用服务时落脏数据
+        if (StringUtils.isEmpty(realName) || StringUtils.isEmpty(idCard))
+        {
+            throw new ServiceException("姓名与身份证号不能为空");
+        }
+        // STEP 3: 防重闸门——该会员已有待审核申请时拒绝,避免后台审核列表堆积重复单
+        if (certificationApplyMapper.countPendingPersonalApplyByUserId(userId) > 0)
+        {
+            throw new ServiceException("已有实名认证申请正在审核中,请勿重复提交");
+        }
+        // STEP 4: 组装申请快照并落库(主体类型固定「个人」;审核状态置「待审核」;
+        //         身份证号统一转大写,兼容末位小写 x)
+        String operator = SecurityUtils.getUsername();
+        AccountCertificationApply apply = new AccountCertificationApply();
+        apply.setUserId(userId);
+        apply.setSubjectType(SUBJECT_PERSONAL);
+        apply.setRealName(realName.trim());
+        apply.setIdCard(idCard.trim().toUpperCase());
+        apply.setAuditStatus(AUDIT_PENDING);
+        apply.setCreateBy(operator);
+        if (certificationApplyMapper.insertCertificationApply(apply) <= 0)
+        {
+            throw new ServiceException("提交实名认证失败,请稍后重试");
+        }
+        // STEP 5: 联动 account_user.auth_status 为「审核中」,个人中心与会员列表据此显示
+        AccountUser user = new AccountUser();
+        user.setUserId(userId);
+        user.setAuthStatus(AUTH_AUDITING);
+        user.setUpdateBy(operator);
+        accountUserMapper.updateUserAuthStatus(user);
+    }
+
+    @Override
+    public AccountCertificationStatusVo getCertificationStatus(Long userId)
+    {
+        AccountCertificationStatusVo vo = new AccountCertificationStatusVo();
+        // 登录态缺失视为从未提交:返回空对象,前端按「未认证」渲染空表单(不返回 null)
+        if (StringUtils.isNull(userId))
+        {
+            return vo;
+        }
+        AccountCertificationApply apply = certificationApplyMapper.selectLatestPersonalApplyByUserId(userId);
+        if (StringUtils.isNull(apply))
+        {
+            return vo;
+        }
+        // 回填最近一次申请的资料与状态:认证失败时前端据 auditRemark 展示驳回原因
+        vo.setRealName(apply.getRealName());
+        vo.setIdCard(apply.getIdCard());
+        vo.setAuditStatus(apply.getAuditStatus());
+        vo.setAuditRemark(apply.getAuditRemark());
+        return vo;
+    }
+
+    @Override
+    @Transactional
+    public void submitCompanyCertification(Long userId, CompanyCertificationApplyDto dto)
+    {
+        // STEP 1: 校验会员ID(登录态缺失时直接失败,避免无归属写入)
+        if (StringUtils.isNull(userId))
+        {
+            throw new ServiceException("提交企业认证失败");
+        }
+        // STEP 2: 入参兜底:Controller 已用 @Validated 拦过,这里防止绕过 Controller 直接落脏数据
+        if (StringUtils.isNull(dto))
+        {
+            throw new ServiceException("认证资料不能为空");
+        }
+        // STEP 3: 有效期必填兜底——「短期」必须填起止日期,「长期」允许为空(前端此时隐藏该输入)
+        checkValidPeriod(dto.getLicenseValidType(), dto.getLicenseValidPeriod(), "营业执照");
+        checkValidPeriod(dto.getLegalIdValidType(), dto.getLegalIdValidPeriod(), "法人身份证");
+        // STEP 4: 防重闸门——该会员已有待审核的企业申请时拒绝,避免后台审核列表堆积重复单
+        if (certificationApplyMapper.countPendingCompanyApplyByUserId(userId) > 0)
+        {
+            throw new ServiceException("已有企业认证申请正在审核中,请勿重复提交");
+        }
+        // STEP 5: 组装申请快照(主体类型取入参 2/3;审核状态置「待审核」)
+        String operator = SecurityUtils.getUsername();
+        AccountCertificationApply apply = new AccountCertificationApply();
+        apply.setUserId(userId);
+        apply.setSubjectType(dto.getSubjectType());
+        apply.setCompanyName(dto.getCompanyName().trim());
+        apply.setLicenseImage(dto.getLicenseImage());
+        apply.setCreditCode(dto.getCreditCode().trim());
+        apply.setLicenseValidType(dto.getLicenseValidType());
+        apply.setLicenseValidPeriod(dto.getLicenseValidPeriod());
+        apply.setRegisteredRegion(dto.getRegisteredRegion());
+        apply.setCompanyAddress(dto.getCompanyAddress());
+        apply.setLegalPerson(dto.getLegalPerson().trim());
+        apply.setLegalIdCardFront(dto.getLegalIdCardFront());
+        apply.setLegalIdCardBack(dto.getLegalIdCardBack());
+        apply.setLegalIdValidType(dto.getLegalIdValidType());
+        apply.setLegalIdValidPeriod(dto.getLegalIdValidPeriod());
+        apply.setLegalIdAddress(dto.getLegalIdAddress());
+        apply.setContactName(dto.getContactName().trim());
+        apply.setContactPhone(dto.getContactPhone().trim());
+        apply.setContactEmail(dto.getContactEmail().trim());
+        apply.setPayeeType(dto.getPayeeType());
+        apply.setBankCardImage(dto.getBankCardImage());
+        apply.setBankCardNo(dto.getBankCardNo().trim());
+        apply.setBankCity(dto.getBankCity());
+        apply.setAuditStatus(AUDIT_PENDING);
+        apply.setCreateBy(operator);
+        // STEP 6: 落库
+        if (certificationApplyMapper.insertCertificationApply(apply) <= 0)
+        {
+            throw new ServiceException("提交企业认证失败,请稍后重试");
+        }
+        // STEP 7: 联动 account_user.company_auth_status 为「审核中」(与个人 auth_status 相互独立)
+        AccountUser user = new AccountUser();
+        user.setUserId(userId);
+        user.setCompanyAuthStatus(AUTH_AUDITING);
+        user.setUpdateBy(operator);
+        accountUserMapper.updateUserCompanyAuthStatus(user);
+    }
+
+    @Override
+    public CompanyCertificationStatusVo getCompanyCertificationStatus(Long userId)
+    {
+        CompanyCertificationStatusVo vo = new CompanyCertificationStatusVo();
+        // 登录态缺失视为从未提交:返回空对象,前端按「未认证」渲染空表单(不返回 null)
+        if (StringUtils.isNull(userId))
+        {
+            return vo;
+        }
+        AccountCertificationApply apply = certificationApplyMapper.selectLatestCompanyApplyByUserId(userId);
+        if (StringUtils.isNull(apply))
+        {
+            return vo;
+        }
+        // 一次性回填整份资料与审核状态:审核中进占位页、已驳回展示原因并允许修改重提
+        vo.setSubjectType(apply.getSubjectType());
+        vo.setCompanyName(apply.getCompanyName());
+        vo.setLicenseImage(apply.getLicenseImage());
+        vo.setCreditCode(apply.getCreditCode());
+        vo.setLicenseValidType(apply.getLicenseValidType());
+        vo.setLicenseValidPeriod(apply.getLicenseValidPeriod());
+        vo.setRegisteredRegion(apply.getRegisteredRegion());
+        vo.setCompanyAddress(apply.getCompanyAddress());
+        vo.setLegalPerson(apply.getLegalPerson());
+        vo.setLegalIdCardFront(apply.getLegalIdCardFront());
+        vo.setLegalIdCardBack(apply.getLegalIdCardBack());
+        vo.setLegalIdValidType(apply.getLegalIdValidType());
+        vo.setLegalIdValidPeriod(apply.getLegalIdValidPeriod());
+        vo.setLegalIdAddress(apply.getLegalIdAddress());
+        vo.setContactName(apply.getContactName());
+        vo.setContactPhone(apply.getContactPhone());
+        vo.setContactEmail(apply.getContactEmail());
+        vo.setPayeeType(apply.getPayeeType());
+        vo.setBankCardImage(apply.getBankCardImage());
+        vo.setBankCardNo(apply.getBankCardNo());
+        vo.setBankCity(apply.getBankCity());
+        vo.setAuditStatus(apply.getAuditStatus());
+        vo.setAuditRemark(apply.getAuditRemark());
+        return vo;
+    }
+
+    /**
+     * 判定主体类型是否为企业/个体工商户(subject_type ∈ {2,3})。
+     */
+    private boolean isCompanySubject(String subjectType)
+    {
+        return SUBJECT_INDIVIDUAL.equals(subjectType) || SUBJECT_COMPANY.equals(subjectType);
+    }
+
+    /**
+     * 有效期必填兜底:类型为「短期」时,起止日期不得为空。
+     *
+     * @param validType   有效期类型(1长期 2短期)
+     * @param validPeriod 有效期起止(文本)
+     * @param label       字段中文名,用于拼接错误提示
+     */
+    private void checkValidPeriod(String validType, String validPeriod, String label)
+    {
+        if (VALID_TYPE_SHORT_TERM.equals(validType) && StringUtils.isEmpty(validPeriod))
+        {
+            throw new ServiceException("请选择" + label + "有效期起止日期");
+        }
     }
 
     /**
@@ -144,6 +371,19 @@
         certification.setRegisterDate(apply.getRegisterDate());
         certification.setLicenseValidPeriod(apply.getLicenseValidPeriod());
         certification.setLicenseImage(apply.getLicenseImage());
+        certification.setLicenseValidType(apply.getLicenseValidType());
+        certification.setLegalIdCardFront(apply.getLegalIdCardFront());
+        certification.setLegalIdCardBack(apply.getLegalIdCardBack());
+        certification.setLegalIdValidType(apply.getLegalIdValidType());
+        certification.setLegalIdValidPeriod(apply.getLegalIdValidPeriod());
+        certification.setLegalIdAddress(apply.getLegalIdAddress());
+        certification.setContactName(apply.getContactName());
+        certification.setContactPhone(apply.getContactPhone());
+        certification.setContactEmail(apply.getContactEmail());
+        certification.setPayeeType(apply.getPayeeType());
+        certification.setBankCardImage(apply.getBankCardImage());
+        certification.setBankCardNo(apply.getBankCardNo());
+        certification.setBankCity(apply.getBankCity());
         certification.setCreateBy(operator);
         certification.setRemark("由认证申请(申请号 " + apply.getId() + ")审核通过生成");
         certificationMapper.insertCertification(certification);

--
Gitblit v1.7.1