package com.ruoyi.user.controller; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.exception.ServiceException; import com.ruoyi.common.core.web.controller.BaseController; import com.ruoyi.common.core.web.page.TableDataInfo; import com.ruoyi.common.security.utils.SecurityUtils; import com.ruoyi.user.domain.AccountAddress; import com.ruoyi.user.domain.dto.AccountAddressDto; import com.ruoyi.user.domain.dto.AccountCertificationApplyDto; import com.ruoyi.user.domain.dto.AccountUserAvatarDto; import com.ruoyi.user.domain.dto.CompanyCertificationApplyDto; import com.ruoyi.user.domain.vo.AccountAddressVo; import com.ruoyi.user.domain.vo.AccountCertificationStatusVo; import com.ruoyi.user.domain.vo.AccountInviteeVo; import com.ruoyi.user.domain.vo.AccountReferrerVo; import com.ruoyi.user.domain.vo.AccountUserInfoVo; import com.ruoyi.user.domain.vo.CompanyCertificationStatusVo; import com.ruoyi.user.domain.vo.InviteCodeVo; import com.ruoyi.user.service.IAccountCertificationApplyService; import com.ruoyi.user.service.IAccountUserService; /** * C端会员 个人中心接口 * * 网关前缀 /miniapp/account 由 StripPrefix=2 剥掉,故此处映射为 /user, * 外部实际路径为 /miniapp/account/user/**。 * * 会员身份取自网关注入的登录态(SecurityUtils.getUserId),不接受前端传 userId, * 天然防止水平越权;未登录请求由网关直接以 401 拦截,故此处不处理未登录分支。 * * @author hugray */ @RestController @RequestMapping("/user") public class AccountProfileController extends BaseController { @Autowired private IAccountUserService accountUserService; @Autowired private IAccountCertificationApplyService accountCertificationApplyService; /** * 查询当前登录会员的个人中心信息 * * 返回昵称、头像、会员等级与收益汇总(今日 / 本月 / 累计,单位分)。 * * @return 会员信息展示对象 */ @GetMapping("/info") public R info() { return R.ok(accountUserService.getAccountUserInfo(SecurityUtils.getUserId())); } /** * 更新当前登录会员头像 * * 头像文件由小程序直传文件服务(POST /file/upload)后,将返回的 url 通过本接口写回; * 会员身份取自登录态,不接受前端传入 userId。 * * @param dto 头像更新入参(仅含 avatar) * @return 操作结果 */ @PostMapping("/avatar") public R updateAvatar(@Validated @RequestBody AccountUserAvatarDto dto) { accountUserService.updateAvatar(SecurityUtils.getUserId(), dto.getAvatar()); return R.ok(); } /** * 查询当前登录会员的实名认证状态 * * 供实名认证页进入时回填:姓名、身份证号、审核状态(0待审核/1已通过/2已驳回)与驳回原因。 * 从未提交过申请时返回各字段为空的对象,前端按「未认证」渲染空表单。 * 会员身份取自登录态,不接受前端传入 userId。 * * @return 认证状态(realName / idCard / auditStatus / auditRemark) */ @GetMapping("/certification") public R certification() { return R.ok(accountCertificationApplyService.getCertificationStatus(SecurityUtils.getUserId())); } /** * 提交实名认证(个人主体) * * 只做基础的姓名必填与身份证号格式校验(@Validated 拦下非法入参),不做三方实名核验; * 提交后落一条待审核的认证申请,出现在管理后台「主体认证审核」列表。 * 会员身份取自登录态,不接受前端传入 userId;已有待审核申请时拒绝重复提交。 * * @param dto 认证申请入参(仅含 realName、idCard) * @return 操作结果 */ @PostMapping("/certification") public R submitCertification(@Validated @RequestBody AccountCertificationApplyDto dto) { accountCertificationApplyService.submitPersonalCertification( SecurityUtils.getUserId(), dto.getRealName(), dto.getIdCard()); return R.ok(); } /** * 查询当前登录会员的企业/个体认证状态 * * 供企业认证向导页进入时回填:整份企业资料 + 审核状态(0待审核/1已通过/2已驳回)与驳回原因。 * 从未提交过申请时返回各字段为空的对象,前端按「未认证」渲染空表单。 * 会员身份取自登录态,不接受前端传入 userId。 * * @return 企业/个体认证状态(整份资料 + auditStatus / auditRemark) */ @GetMapping("/company-certification") public R companyCertification() { return R.ok(accountCertificationApplyService.getCompanyCertificationStatus(SecurityUtils.getUserId())); } /** * 提交企业/个体工商户认证(主体类型 2/3) * * 只做最基础的必填校验(@Validated 拦下空值与非法枚举),不做格式与三方核验,真实性由后台人工审核把关; * 提交后落一条待审核的认证申请,出现在管理后台「主体认证审核」列表。 * 会员身份取自登录态,不接受前端传入 userId;已有待审核的企业申请时拒绝重复提交。 * * @param dto 企业认证申请入参 * @return 操作结果 */ @PostMapping("/company-certification") public R submitCompanyCertification(@Validated @RequestBody CompanyCertificationApplyDto dto) { accountCertificationApplyService.submitCompanyCertification(SecurityUtils.getUserId(), dto); return R.ok(); } /** * 查询本人邀请码 * * 供「我的邀请」页「邀请码」Tab 展示;会员身份取自登录态,不接受前端传入 userId。 * * @return 邀请码展示对象 */ @GetMapping("/invite-code") public R inviteCode() { return R.ok(accountUserService.getInviteCode(SecurityUtils.getUserId())); } /** * 查询本人直接下级(被邀请人)列表 * * 分页返回(pageNum 默认 1、pageSize 默认 10),按绑定时间倒序; * 会员身份取自登录态,不接受前端传入 userId。 * * @return 分页数据(rows 为 AccountInviteeVo 列表,total 为总数) */ @GetMapping("/invitees") public TableDataInfo invitees() { startPage(); List list = accountUserService.getInvitees(SecurityUtils.getUserId()); return getDataTable(list); } /** * 查询本人上级(推荐人)信息 * * 供「我的邀请」页「邀请我的」Tab 展示;无上级时返回各字段为 null 的对象; * 会员身份取自登录态,不接受前端传入 userId。 * * @return 上级展示对象 */ @GetMapping("/referrer") public R referrer() { return R.ok(accountUserService.getReferrer(SecurityUtils.getUserId())); } /** * 查询本人收货地址列表 * * 默认地址排前,其余按 id 升序;地址数量少,不分页。 * 会员身份取自登录态,不接受前端传入 userId。 * * @return 地址列表(无地址返回空集合) */ @GetMapping("/addresses") public R> addressList() { List list = accountUserService.getAddressList(SecurityUtils.getUserId()); return R.ok(toAddressVoList(list)); } /** * 查询本人单条收货地址详情 * * 供编辑页回填;校验归属,非本人地址抛业务异常。 * 会员身份取自登录态,不接受前端传入 userId。 * * @param id 地址ID * @return 地址详情 */ @GetMapping("/addresses/{id}") public R addressDetail(@PathVariable("id") Long id) { AccountAddress address = accountUserService.getAddressDetail(SecurityUtils.getUserId(), id); return R.ok(toAddressVo(address)); } /** * 新增本人收货地址 * * 首条地址自动设为默认;会员身份取自登录态,不接受前端传入 userId。 * * @param dto 地址入参 * @return 操作结果 */ @PostMapping("/addresses") public R createAddress(@Validated @RequestBody AccountAddressDto dto) { accountUserService.insertMyAddress(buildAddress(dto, SecurityUtils.getUserId())); return R.ok(); } /** * 编辑本人收货地址 * * 复用后台改地址事务逻辑(含默认唯一约束);会员身份取自登录态。 * * @param dto 地址入参(必含 id) * @return 操作结果 */ @PutMapping("/addresses") public R updateAddress(@Validated @RequestBody AccountAddressDto dto) { if (dto.getId() == null) { throw new ServiceException("地址ID不能为空"); } accountUserService.updateAccountAddress(buildAddress(dto, SecurityUtils.getUserId())); return R.ok(); } /** * 删除本人收货地址 * * 校验归属后逻辑删除;会员身份取自登录态,不接受前端传入 userId。 * * @param id 地址ID * @return 操作结果 */ @DeleteMapping("/addresses/{id}") public R deleteAddress(@PathVariable("id") Long id) { accountUserService.deleteMyAddress(SecurityUtils.getUserId(), id); return R.ok(); } /** * DTO 转实体(userId 由登录态注入,不接受前端传值) */ private AccountAddress buildAddress(AccountAddressDto dto, Long userId) { AccountAddress address = new AccountAddress(); address.setId(dto.getId()); address.setUserId(userId); address.setConsignee(dto.getConsignee()); address.setConsigneePhone(dto.getConsigneePhone()); address.setAddress(dto.getAddress()); address.setIsDefault(dto.getIsDefault()); return address; } /** * 实体转出参 VO(列表) */ private List toAddressVoList(List list) { List voList = new ArrayList<>(list.size()); for (AccountAddress address : list) { voList.add(toAddressVo(address)); } return voList; } /** * 实体转出参 VO(单条) */ private AccountAddressVo toAddressVo(AccountAddress address) { AccountAddressVo vo = new AccountAddressVo(); vo.setId(address.getId()); vo.setConsignee(address.getConsignee()); vo.setConsigneePhone(address.getConsigneePhone()); vo.setAddress(address.getAddress()); vo.setIsDefault(address.getIsDefault()); return vo; } }