Bläddra i källkod

1.获取当前用户头像、昵称、手机号、实名认证标识
2.忘记登录密码/重置密码
3.修改登录密码
4.绑定手机号
5.mock real name verify
6.修改头像、昵称等个人信息
7.提供oidc相关api

hr~ 1 månad sedan
förälder
incheckning
604f659d3f
26 ändrade filer med 1821 tillägg och 0 borttagningar
  1. 1 0
      .gitignore
  2. 1 0
      pom.xml
  3. 26 0
      poyee-account/pom.xml
  4. 80 0
      poyee-account/src/main/java/com/tzy/controller/AppAccountController.java
  5. 63 0
      poyee-account/src/main/java/com/tzy/controller/RemoteEndUserController.java
  6. 26 0
      poyee-account/src/main/java/com/tzy/dto/AccountProfileInfoDTO.java
  7. 77 0
      poyee-account/src/main/java/com/tzy/dto/EndUserDTO.java
  8. 127 0
      poyee-account/src/main/java/com/tzy/entity/AppAccount.java
  9. 414 0
      poyee-account/src/main/java/com/tzy/entity/AppBaseUser.java
  10. 18 0
      poyee-account/src/main/java/com/tzy/mapper/PoyeeAppAccountMapper.java
  11. 20 0
      poyee-account/src/main/java/com/tzy/mapper/PoyeeAppBaseUserMapper.java
  12. 18 0
      poyee-account/src/main/java/com/tzy/req/AccountPasswordRequest.java
  13. 18 0
      poyee-account/src/main/java/com/tzy/req/AppleIdCreateRequest.java
  14. 15 0
      poyee-account/src/main/java/com/tzy/req/BindPhoneRequest.java
  15. 17 0
      poyee-account/src/main/java/com/tzy/req/EmailCreateRequest.java
  16. 18 0
      poyee-account/src/main/java/com/tzy/req/EmailPasswordCreateRequest.java
  17. 15 0
      poyee-account/src/main/java/com/tzy/req/PhoneCreateRequest.java
  18. 32 0
      poyee-account/src/main/java/com/tzy/req/ProfileUpdateRequest.java
  19. 18 0
      poyee-account/src/main/java/com/tzy/req/WeChatCreateRequest.java
  20. 21 0
      poyee-account/src/main/java/com/tzy/req/WeChatPhoneCreateRequest.java
  21. 33 0
      poyee-account/src/main/java/com/tzy/service/AppAccountOidcService.java
  22. 434 0
      poyee-account/src/main/java/com/tzy/service/impl/AppAccountOidcServiceImpl.java
  23. 107 0
      poyee-account/src/main/resources/mapper/AppAccountMapper.xml
  24. 205 0
      poyee-account/src/main/resources/mapper/AppBaseUserMapper.xml
  25. 12 0
      poyee-order/src/main/java/com/tzy/service/impl/LotOrderServiceImpl.java
  26. 5 0
      poyi-app/pom.xml

+ 1 - 0
.gitignore

@@ -18,6 +18,7 @@
 /tzy-system/target/
 /tzy-system/target/
 /weixin-api/target
 /weixin-api/target
 /target/*
 /target/*
+/poyee-account/target
 
 
 ### Java template
 ### Java template
 
 

+ 1 - 0
pom.xml

@@ -166,6 +166,7 @@
         <module>poyi-service</module>
         <module>poyi-service</module>
         <module>poyi-app</module>
         <module>poyi-app</module>
         <module>poyee-order</module>
         <module>poyee-order</module>
+        <module>poyee-account</module>
     </modules>
     </modules>
     <packaging>pom</packaging>
     <packaging>pom</packaging>
 
 

+ 26 - 0
poyee-account/pom.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.tzy</groupId>
+        <artifactId>tzy</artifactId>
+        <version>4.5.0</version>
+    </parent>
+
+    <artifactId>poyee-account</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>com.tzy</groupId>
+            <artifactId>poyi-service</artifactId>
+            <version>4.5.0</version>
+        </dependency>
+    </dependencies>
+</project>

+ 80 - 0
poyee-account/src/main/java/com/tzy/controller/AppAccountController.java

@@ -0,0 +1,80 @@
+package com.tzy.controller;
+
+import com.tzy.common.dto.OutDTO;
+import com.tzy.common.dto.UserInfo;
+import com.tzy.common.utils.UserUtils;
+import com.tzy.dto.AccountProfileInfoDTO;
+import com.tzy.dto.EndUserDTO;
+import com.tzy.req.AccountPasswordRequest;
+import com.tzy.req.BindPhoneRequest;
+import com.tzy.req.ProfileUpdateRequest;
+import com.tzy.service.AppAccountOidcService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@RestController
+@RequestMapping("/api/account")
+@Api(tags = "App账号接口")
+public class AppAccountController {
+
+    @Resource
+    private AppAccountOidcService appAccountOidcService;
+
+    @GetMapping("/profile")
+    @ApiOperation("获取当前用户头像、昵称、手机号、实名认证标识")
+    public OutDTO getCurrentProfile() {
+        UserInfo userInfo = UserUtils.getSimpleUserInfo(true);
+        if (userInfo == null) {
+            return OutDTO.noauth("请登录");
+        }
+        AccountProfileInfoDTO profile = appAccountOidcService.getCurrentProfile(userInfo.getId());
+        return OutDTO.ok().put("user", profile);
+    }
+
+    @PostMapping("/password/reset")
+    @ApiOperation("忘记登录密码/重置密码")
+    public OutDTO resetPassword(@ApiParam(value = "重置密码参数", required = true) @RequestBody AccountPasswordRequest request) {
+        appAccountOidcService.resetPassword(request);
+        return OutDTO.ok();
+    }
+
+    @PostMapping("/password/change")
+    @ApiOperation("修改登录密码")
+    public OutDTO changePassword(@ApiParam(value = "修改密码参数", required = true) @RequestBody AccountPasswordRequest request) {
+        appAccountOidcService.changePassword(request);
+        return OutDTO.ok();
+    }
+
+    @PostMapping("/phone/bind")
+    @ApiOperation("绑定手机号")
+    public OutDTO bindPhone(@ApiParam(value = "绑定手机号参数", required = true) @RequestBody BindPhoneRequest request) {
+        EndUserDTO user = appAccountOidcService.bindPhone(request);
+        return OutDTO.ok().put("user", user);
+    }
+
+    @PostMapping("/realname/mock")
+    @ApiOperation("mock real name verify")
+    public OutDTO mockRealNameVerify() {
+        UserInfo userInfo = UserUtils.getSimpleUserInfo(true);
+        if (userInfo == null) {
+            return OutDTO.noauth("璇风櫥褰?");
+        }
+        AccountProfileInfoDTO profile = appAccountOidcService.mockRealNameVerify(userInfo.getId());
+        return OutDTO.ok().put("user", profile);
+    }
+
+    @PostMapping("/profile")
+    @ApiOperation("修改头像、昵称等个人信息")
+    public OutDTO updateProfile(@ApiParam(value = "个人信息参数", required = true) @RequestBody ProfileUpdateRequest request) {
+        EndUserDTO user = appAccountOidcService.updateProfile(request);
+        return OutDTO.ok().put("user", user);
+    }
+}

+ 63 - 0
poyee-account/src/main/java/com/tzy/controller/RemoteEndUserController.java

@@ -0,0 +1,63 @@
+package com.tzy.controller;
+
+import com.tzy.dto.EndUserDTO;
+import com.tzy.req.AppleIdCreateRequest;
+import com.tzy.req.EmailCreateRequest;
+import com.tzy.req.EmailPasswordCreateRequest;
+import com.tzy.req.PhoneCreateRequest;
+import com.tzy.req.WeChatCreateRequest;
+import com.tzy.req.WeChatPhoneCreateRequest;
+import com.tzy.service.AppAccountOidcService;
+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.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.Optional;
+
+@RestController
+@RequestMapping("/api/user")
+public class RemoteEndUserController {
+
+    @Resource
+    private AppAccountOidcService appAccountOidcService;
+
+    @GetMapping("/loginId/{loginId}")
+    public Optional<EndUserDTO> getUser(@PathVariable("loginId") String loginId) {
+        EndUserDTO user = appAccountOidcService.loadUserByLoginId(loginId);
+        return user == null ? Optional.empty() : Optional.of(user);
+    }
+
+    @PostMapping("/email")
+    public Optional<EndUserDTO> createEmailUser(@RequestBody EmailCreateRequest request) {
+        return Optional.of(appAccountOidcService.createEmailUser(request));
+    }
+
+    @PostMapping("/emailpassword")
+    public Optional<EndUserDTO> createEmailPasswordUser(@RequestBody EmailPasswordCreateRequest request) {
+        return Optional.of(appAccountOidcService.createEmailPasswordUser(request));
+    }
+
+    @PostMapping("/phone")
+    public Optional<EndUserDTO> createPhoneUser(@RequestBody PhoneCreateRequest request) {
+        return Optional.of(appAccountOidcService.createPhoneUser(request));
+    }
+
+    @PostMapping("/wechat")
+    public Optional<EndUserDTO> createWeChatUser(@RequestBody WeChatCreateRequest request) {
+        return Optional.of(appAccountOidcService.createWeChatUser(request));
+    }
+
+    @PostMapping("/wechat/phone")
+    public Optional<EndUserDTO> createWeChatPhoneUser(@RequestBody WeChatPhoneCreateRequest request) {
+        return Optional.of(appAccountOidcService.createWeChatPhoneUser(request));
+    }
+
+    @PostMapping("/appleid")
+    public Optional<EndUserDTO> createAppleIdUser(@RequestBody AppleIdCreateRequest request) {
+        return Optional.of(appAccountOidcService.createAppleIdUser(request));
+    }
+}

+ 26 - 0
poyee-account/src/main/java/com/tzy/dto/AccountProfileInfoDTO.java

@@ -0,0 +1,26 @@
+package com.tzy.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("当前账号基础资料")
+public class AccountProfileInfoDTO {
+    @ApiModelProperty(value = "头像地址", example = "https://img.example/avatar.png")
+    private String avatar;
+
+    @ApiModelProperty(value = "昵称", example = "Alice")
+    private String nickname;
+
+    @ApiModelProperty(value = "手机号", example = "13800000000")
+    private String phone;
+
+    @ApiModelProperty(value = "实名认证标识,1=已实名,其它=未实名", example = "1")
+    private Integer faceVerify;
+    @ApiModelProperty(value = "idCard", example = "110101199001011234")
+    private String idCard;
+
+    @ApiModelProperty(value = "用户ID", example = "1")
+    private Long userId;
+}

+ 77 - 0
poyee-account/src/main/java/com/tzy/dto/EndUserDTO.java

@@ -0,0 +1,77 @@
+package com.tzy.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+@ApiModel("OIDC用户信息")
+public class EndUserDTO {
+    @ApiModelProperty(value = "用户ID", example = "100")
+    private int id;
+
+    @ApiModelProperty(value = "用户名/账号", example = "u100")
+    private String username;
+
+    @ApiModelProperty(value = "邮箱", example = "user@example.com")
+    private String email;
+
+    @ApiModelProperty(value = "手机号", example = "13800000000")
+    private String phone;
+
+    @ApiModelProperty(value = "显示名称", example = "Alice")
+    private String displayName;
+
+    @ApiModelProperty(value = "头像地址", example = "https://img.example/avatar.png")
+    private String avatarUrl;
+
+    @ApiModelProperty(value = "密码Hash")
+    private String passwordHash;
+
+    @ApiModelProperty(value = "密码盐")
+    private String salt;
+
+    @ApiModelProperty(value = "用户编码", example = "U000100")
+    private String code;
+
+    @ApiModelProperty(value = "状态:ACTIVE正常,INACTIVE停用,BANNED封禁", example = "ACTIVE")
+    private String status;
+
+    @ApiModelProperty(value = "身份/注册类型", example = "PHONE")
+    private String identityType;
+
+    @ApiModelProperty(value = "注册时间")
+    private LocalDateTime registrationTime;
+
+    @ApiModelProperty(value = "最后登录时间")
+    private LocalDateTime lastLoginTime;
+
+    @ApiModelProperty(value = "用户扩展资料")
+    private EndUserProfile profile;
+
+    @ApiModelProperty(value = "角色编码列表")
+    private List<String> roleCode;
+
+    @Data
+    @ApiModel("OIDC用户扩展资料")
+    public static class EndUserProfile {
+        @ApiModelProperty(value = "真实姓名", example = "张三")
+        private String realName;
+
+        @ApiModelProperty(value = "性别", example = "1")
+        private String gender;
+
+        @ApiModelProperty(value = "生日")
+        private LocalDate birthday;
+
+        @ApiModelProperty(value = "身份证号")
+        private String idCardNumber;
+
+        @ApiModelProperty(value = "地址")
+        private String address;
+    }
+}

+ 127 - 0
poyee-account/src/main/java/com/tzy/entity/AppAccount.java

@@ -0,0 +1,127 @@
+package com.tzy.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * <p>
+ * 移动端账号管理
+ * </p>
+ *
+ * @author Hr
+ * @since 2026-05-19
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AppAccount implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * id
+     */
+    private Integer id;
+
+    /**
+     * 所属程序
+     */
+    private String appid;
+
+    /**
+     * 账号
+     */
+    private String account;
+
+    /**
+     * 密码
+     */
+    private String password;
+
+    /**
+     * 邮箱
+     */
+    private String email;
+
+    /**
+     * 手机
+     */
+    private String phone;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 状态
+     */
+    private Short status;
+
+    /**
+     * 角色
+     */
+    private Integer roleid;
+
+    /**
+     * 创建人
+     */
+    private String createBy;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 更新人
+     */
+    private String updateBy;
+
+    /**
+     * 更新时间
+     */
+    private Date cupdateTime;
+    private Short delFlg;
+
+    /**
+     * 注销时间
+     */
+    private Date logOffTime;
+
+    /**
+     * 手机区号
+     */
+    private String diallingCode;
+
+    private String salt;
+
+
+    @Override
+    public String toString() {
+        return "AppAccount{" +
+        ", id = " + id +
+        ", appid = " + appid +
+        ", account = " + account +
+        ", password = " + password +
+        ", email = " + email +
+        ", phone = " + phone +
+        ", remark = " + remark +
+        ", status = " + status +
+        ", roleid = " + roleid +
+        ", createBy = " + createBy +
+        ", createTime = " + createTime +
+        ", updateBy = " + updateBy +
+        ", cupdateTime = " + cupdateTime +
+        ", delFlg = " + delFlg +
+        ", logOffTime = " + logOffTime +
+        ", diallingCode = " + diallingCode +
+        "}";
+    }
+}

+ 414 - 0
poyee-account/src/main/java/com/tzy/entity/AppBaseUser.java

@@ -0,0 +1,414 @@
+package com.tzy.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * <p>
+ * 用户信息
+ * </p>
+ *
+ * @author Hr
+ * @since 2026-05-19
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class AppBaseUser implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * id
+     */
+    private Integer id;
+
+    /**
+     * 所属程序
+     */
+    private String appid;
+
+    /**
+     * 真实姓名
+     */
+    private String realname;
+
+    /**
+     * 昵称
+     */
+    private String nickname;
+
+    /**
+     * 头像
+     */
+    private String avatar;
+
+    /**
+     * 积分
+     */
+    private Long point;
+
+    /**
+     * 会员等级
+     */
+    private Short level;
+
+    /**
+     * 生日
+     */
+    private Date birthday;
+
+    /**
+     * 性别
+     */
+    private Short sex;
+
+    /**
+     * openid
+     */
+    private String openid;
+
+    /**
+     * unionid
+     */
+    private String unionid;
+
+    /**
+     * 注册渠道
+     */
+    private String registerChannel;
+
+    /**
+     * 状态
+     */
+    private Short status;
+
+    /**
+     * 删除标记
+     */
+    private Short delFlg;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 创建人
+     */
+    private String createBy;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 更新人
+     */
+    private String updateBy;
+
+    /**
+     * 更新时间
+     */
+    private Date updateTime;
+
+    /**
+     * 账号
+     */
+    private String username;
+
+    /**
+     * 会员成长值
+     */
+    private Integer growthNum;
+
+    /**
+     * 会员码
+     */
+    private String code;
+
+    /**
+     * 推送是否接受
+     */
+    private Short notifyFlag;
+
+    /**
+     * 极光id
+     */
+    private String smsRegisterId;
+    private Integer userId;
+    private String notifyType;
+
+    /**
+     * 通过人脸识别标志:1
+     */
+    private Integer faceVerify;
+
+    /**
+     * 支付开关
+     */
+    private Integer openPsd;
+
+    /**
+     * 支付密码
+     */
+    private String payPsd;
+
+    /**
+     * 登陆密码
+     */
+    private String loginPsd;
+
+    /**
+     * 是否拒绝自提,1拒绝0同意
+     */
+    private Integer refusePickUp;
+
+    /**
+     * 额外配置
+     */
+    private String prop1;
+
+    /**
+     * 备用
+     */
+    private String prop2;
+
+    /**
+     * 备用
+     */
+    private String prop3;
+
+    /**
+     * 备用
+     */
+    private String prop4;
+
+    /**
+     * 悬浮窗口开关
+     */
+    private Short windowOpen;
+
+    /**
+     * 身份证姓名
+     */
+    private String certName;
+
+    /**
+     * 支付宝账号
+     */
+    private String alipayAccount;
+
+    /**
+     * 开票权限
+     */
+    private Short openInvoice;
+
+    /**
+     * 编辑黑名单,1不允许编辑,2:限制云闪付
+     */
+    private Short blacklist;
+
+    /**
+     * 身份证号
+     */
+    private String idCard;
+
+    /**
+     * 会员等级
+     */
+    private Short memberLevel;
+
+    /**
+     * 会员名称
+     */
+    private String memberName;
+
+    /**
+     * 当月已获取成长值
+     */
+    private Integer currentMonthGrowth;
+
+    /**
+     * 月初初始化标志,默认0:未开始,1:已初始化
+     */
+    private Short memberInitFlag;
+
+    /**
+     * 当前会员保级所需成长值
+     */
+    private Integer memberKeepGrowth;
+
+    /**
+     * 用户注册ip
+     */
+    private String registerIpAddr;
+
+    /**
+     * 用户注册省区
+     */
+    private String registerAddr;
+
+    /**
+     * 用户上一次登陆ip
+     */
+    private String loginIpAddr;
+
+    /**
+     * 用户上一次登陆省区
+     */
+    private String loginAddr;
+
+    /**
+     * 用户身份信息
+     */
+    private String userCertData;
+
+    /**
+     * app顶部横幅通知
+     */
+    private Short notifyTopShow;
+
+    /**
+     * 声音提醒,默认1:开启,0:关闭
+     */
+    private Short voiceReminder;
+
+    /**
+     * 震动提醒,默认1:开启,0:关闭
+     */
+    private Short vibrateReminder;
+
+    /**
+     * 消费总金额
+     */
+    private BigDecimal consumeAmount;
+
+    /**
+     * 订单总数
+     */
+    private Integer orderTotalNum;
+
+    /**
+     * 开卡特效开关,默认1:开,0:关
+     */
+    private Short openCardShow;
+    private String effectsType;
+
+    /**
+     * 直播间配置json
+     */
+    private String liveConfigJson;
+
+    /**
+     * 苹果主体转换后的sub
+     */
+    private String transferSubId;
+
+    /**
+     * 取消实名认证次数
+     */
+    private Integer cancelVerifyNum;
+    private Integer version;
+
+    /**
+     * 每日限额提醒
+     */
+    private Integer dailyLimit;
+
+    /**
+     * 每周限额提醒
+     */
+    private Integer weeklyLimit;
+
+    /**
+     * 每月限额
+     */
+    private Integer monthlyLimit;
+
+    /**
+     * 直播间匿名观看
+     */
+    private Short liveAnonymous;
+
+
+    @Override
+    public String toString() {
+        return "AppBaseUser{" +
+        ", id = " + id +
+        ", appid = " + appid +
+        ", realname = " + realname +
+        ", nickname = " + nickname +
+        ", avatar = " + avatar +
+        ", point = " + point +
+        ", level = " + level +
+        ", birthday = " + birthday +
+        ", sex = " + sex +
+        ", openid = " + openid +
+        ", unionid = " + unionid +
+        ", registerChannel = " + registerChannel +
+        ", status = " + status +
+        ", delFlg = " + delFlg +
+        ", remark = " + remark +
+        ", createBy = " + createBy +
+        ", createTime = " + createTime +
+        ", updateBy = " + updateBy +
+        ", updateTime = " + updateTime +
+        ", username = " + username +
+        ", growthNum = " + growthNum +
+        ", code = " + code +
+        ", notifyFlag = " + notifyFlag +
+        ", smsRegisterId = " + smsRegisterId +
+        ", userId = " + userId +
+        ", notifyType = " + notifyType +
+        ", faceVerify = " + faceVerify +
+        ", openPsd = " + openPsd +
+        ", payPsd = " + payPsd +
+        ", loginPsd = " + loginPsd +
+        ", refusePickUp = " + refusePickUp +
+        ", prop1 = " + prop1 +
+        ", prop2 = " + prop2 +
+        ", prop3 = " + prop3 +
+        ", prop4 = " + prop4 +
+        ", windowOpen = " + windowOpen +
+        ", certName = " + certName +
+        ", alipayAccount = " + alipayAccount +
+        ", openInvoice = " + openInvoice +
+        ", blacklist = " + blacklist +
+        ", idCard = " + idCard +
+        ", memberLevel = " + memberLevel +
+        ", memberName = " + memberName +
+        ", currentMonthGrowth = " + currentMonthGrowth +
+        ", memberInitFlag = " + memberInitFlag +
+        ", memberKeepGrowth = " + memberKeepGrowth +
+        ", registerIpAddr = " + registerIpAddr +
+        ", registerAddr = " + registerAddr +
+        ", loginIpAddr = " + loginIpAddr +
+        ", loginAddr = " + loginAddr +
+        ", userCertData = " + userCertData +
+        ", notifyTopShow = " + notifyTopShow +
+        ", voiceReminder = " + voiceReminder +
+        ", vibrateReminder = " + vibrateReminder +
+        ", consumeAmount = " + consumeAmount +
+        ", orderTotalNum = " + orderTotalNum +
+        ", openCardShow = " + openCardShow +
+        ", effectsType = " + effectsType +
+        ", liveConfigJson = " + liveConfigJson +
+        ", transferSubId = " + transferSubId +
+        ", cancelVerifyNum = " + cancelVerifyNum +
+        ", version = " + version +
+        ", dailyLimit = " + dailyLimit +
+        ", weeklyLimit = " + weeklyLimit +
+        ", monthlyLimit = " + monthlyLimit +
+        ", liveAnonymous = " + liveAnonymous +
+        "}";
+    }
+}

+ 18 - 0
poyee-account/src/main/java/com/tzy/mapper/PoyeeAppAccountMapper.java

@@ -0,0 +1,18 @@
+package com.tzy.mapper;
+
+import com.tzy.entity.AppAccount;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface PoyeeAppAccountMapper {
+    AppAccount selectByLoginId(@Param("loginId") String loginId);
+
+    int insertAppAccount(AppAccount appAccount);
+
+    int updatePassword(@Param("account") String account, @Param("password") String password, @Param("salt") String salt);
+
+    int updatePhone(@Param("account") String account, @Param("phone") String phone);
+
+    List<String> selectRoleCodesByRoleId(@Param("roleid") Integer roleid);
+}

+ 20 - 0
poyee-account/src/main/java/com/tzy/mapper/PoyeeAppBaseUserMapper.java

@@ -0,0 +1,20 @@
+package com.tzy.mapper;
+
+import com.tzy.entity.AppBaseUser;
+import org.apache.ibatis.annotations.Param;
+
+public interface PoyeeAppBaseUserMapper {
+    AppBaseUser selectById(@Param("id") Integer id);
+
+    AppBaseUser selectByUsername(@Param("username") String username);
+
+    AppBaseUser selectByExternalLoginId(@Param("loginId") String loginId);
+
+    int insertAppBaseUser(AppBaseUser appBaseUser);
+
+    int updateAppBaseUser(AppBaseUser appBaseUser);
+
+    int updateRealNameVerify(@Param("userId") Integer userId,
+                             @Param("faceVerify") Integer faceVerify,
+                             @Param("idCard") String idCard);
+}

+ 18 - 0
poyee-account/src/main/java/com/tzy/req/AccountPasswordRequest.java

@@ -0,0 +1,18 @@
+package com.tzy.req;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("账号密码请求")
+public class AccountPasswordRequest {
+    @ApiModelProperty(value = "登录标识,支持账号/手机号/邮箱", example = "u100")
+    private String loginId;
+
+    @ApiModelProperty(value = "旧密码,修改密码时必填", example = "oldPassword123")
+    private String oldPassword;
+
+    @ApiModelProperty(value = "新密码,重置或修改密码时必填", example = "newPassword123")
+    private String newPassword;
+}

+ 18 - 0
poyee-account/src/main/java/com/tzy/req/AppleIdCreateRequest.java

@@ -0,0 +1,18 @@
+package com.tzy.req;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("苹果账号创建请求")
+public class AppleIdCreateRequest {
+    @ApiModelProperty(value = "Apple用户唯一标识", required = true, example = "001234.apple")
+    private String appleId;
+
+    @ApiModelProperty(value = "邮箱", example = "user@example.com")
+    private String email;
+
+    @ApiModelProperty(value = "头像地址", example = "https://img.example/avatar.png")
+    private String avatar;
+}

+ 15 - 0
poyee-account/src/main/java/com/tzy/req/BindPhoneRequest.java

@@ -0,0 +1,15 @@
+package com.tzy.req;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("绑定手机号请求")
+public class BindPhoneRequest {
+    @ApiModelProperty(value = "登录标识,支持账号/手机号/邮箱", required = true, example = "u100")
+    private String loginId;
+
+    @ApiModelProperty(value = "待绑定手机号", required = true, example = "13800000000")
+    private String phone;
+}

+ 17 - 0
poyee-account/src/main/java/com/tzy/req/EmailCreateRequest.java

@@ -0,0 +1,17 @@
+package com.tzy.req;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Optional;
+
+@Data
+@ApiModel("邮箱账号创建请求")
+public class EmailCreateRequest {
+    @ApiModelProperty(value = "邮箱", required = true, example = "user@example.com")
+    private String email;
+
+    @ApiModelProperty(value = "显示名称", example = "Alice")
+    private Optional<String> displayName = Optional.empty();
+}

+ 18 - 0
poyee-account/src/main/java/com/tzy/req/EmailPasswordCreateRequest.java

@@ -0,0 +1,18 @@
+package com.tzy.req;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("邮箱密码账号创建请求")
+public class EmailPasswordCreateRequest {
+    @ApiModelProperty(value = "密码", required = true, example = "password123")
+    private String password;
+
+    @ApiModelProperty(value = "邮箱", required = true, example = "user@example.com")
+    private String email;
+
+    @ApiModelProperty(value = "显示名称", example = "Alice")
+    private String displayName;
+}

+ 15 - 0
poyee-account/src/main/java/com/tzy/req/PhoneCreateRequest.java

@@ -0,0 +1,15 @@
+package com.tzy.req;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("手机号账号创建请求")
+public class PhoneCreateRequest {
+    @ApiModelProperty(value = "手机号", required = true, example = "13800000000")
+    private String phone;
+
+    @ApiModelProperty(value = "注册渠道:THIRD_APK=安卓手机号注册,THIRD_APP=苹果手机号注册;不传默认THIRD_APK", example = "THIRD_APK")
+    private String registerChannel;
+}

+ 32 - 0
poyee-account/src/main/java/com/tzy/req/ProfileUpdateRequest.java

@@ -0,0 +1,32 @@
+package com.tzy.req;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@ApiModel("个人信息修改请求")
+public class ProfileUpdateRequest {
+    @ApiModelProperty(value = "用户ID,和loginId至少传一个", example = "100")
+    private Integer userId;
+
+    @ApiModelProperty(value = "登录标识,和userId至少传一个", example = "u100")
+    private String loginId;
+
+    @ApiModelProperty(value = "昵称", example = "Alice")
+    private String nickname;
+
+    @ApiModelProperty(value = "头像地址", example = "https://img.example/avatar.png")
+    private String avatar;
+
+    @ApiModelProperty(value = "真实姓名", example = "张三")
+    private String realname;
+
+    @ApiModelProperty(value = "生日")
+    private Date birthday;
+
+    @ApiModelProperty(value = "性别", example = "1")
+    private Short sex;
+}

+ 18 - 0
poyee-account/src/main/java/com/tzy/req/WeChatCreateRequest.java

@@ -0,0 +1,18 @@
+package com.tzy.req;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("微信账号创建请求")
+public class WeChatCreateRequest {
+    @ApiModelProperty(value = "微信openId", required = true, example = "wx-open-id")
+    private String openId;
+
+    @ApiModelProperty(value = "微信昵称", example = "微信用户")
+    private String nickName;
+
+    @ApiModelProperty(value = "微信头像地址", example = "https://img.example/wx-avatar.png")
+    private String avatarUrl;
+}

+ 21 - 0
poyee-account/src/main/java/com/tzy/req/WeChatPhoneCreateRequest.java

@@ -0,0 +1,21 @@
+package com.tzy.req;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel("微信手机号账号创建请求")
+public class WeChatPhoneCreateRequest {
+    @ApiModelProperty(value = "微信openId", required = true, example = "wx-open-id")
+    private String openId;
+
+    @ApiModelProperty(value = "手机号", required = true, example = "13800000000")
+    private String phone;
+
+    @ApiModelProperty(value = "头像地址", example = "https://img.example/wx-avatar.png")
+    private String avatarUrl;
+
+    @ApiModelProperty(value = "昵称", example = "微信用户")
+    private String nickName;
+}

+ 33 - 0
poyee-account/src/main/java/com/tzy/service/AppAccountOidcService.java

@@ -0,0 +1,33 @@
+package com.tzy.service;
+
+import com.tzy.dto.EndUserDTO;
+import com.tzy.dto.AccountProfileInfoDTO;
+import com.tzy.req.*;
+
+public interface AppAccountOidcService {
+    EndUserDTO loadUserByLoginId(String loginId);
+
+    EndUserDTO createEmailUser(EmailCreateRequest request);
+
+    EndUserDTO createEmailPasswordUser(EmailPasswordCreateRequest request);
+
+    EndUserDTO createPhoneUser(PhoneCreateRequest request);
+
+    EndUserDTO createWeChatUser(WeChatCreateRequest request);
+
+    EndUserDTO createWeChatPhoneUser(WeChatPhoneCreateRequest request);
+
+    EndUserDTO createAppleIdUser(AppleIdCreateRequest request);
+
+    void resetPassword(AccountPasswordRequest request);
+
+    void changePassword(AccountPasswordRequest request);
+
+    EndUserDTO bindPhone(BindPhoneRequest request);
+
+    AccountProfileInfoDTO getCurrentProfile(Integer userId);
+
+    AccountProfileInfoDTO mockRealNameVerify(Integer userId);
+
+    EndUserDTO updateProfile(ProfileUpdateRequest request);
+}

+ 434 - 0
poyee-account/src/main/java/com/tzy/service/impl/AppAccountOidcServiceImpl.java

@@ -0,0 +1,434 @@
+package com.tzy.service.impl;
+
+import com.tzy.common.exception.ServiceException;
+import com.tzy.common.utils.UserType;
+import com.tzy.dto.AccountProfileInfoDTO;
+import com.tzy.dto.EndUserDTO;
+import com.tzy.entity.AppAccount;
+import com.tzy.entity.AppBaseUser;
+import com.tzy.mapper.PoyeeAppAccountMapper;
+import com.tzy.mapper.PoyeeAppBaseUserMapper;
+import com.tzy.req.*;
+import com.tzy.service.AppAccountOidcService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import javax.annotation.Resource;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+@Service
+public class AppAccountOidcServiceImpl implements AppAccountOidcService {
+
+    private static final String ACTIVE = "ACTIVE";
+    private static final String INACTIVE = "INACTIVE";
+    private static final String BANNED = "BANNED";
+    private static final String DEFAULT_AVATAR = "https://static.public.hobbystock.cn/applet/share/share_logo2.png";
+    private static final int DEFAULT_ROLE_ID = 0;
+    private static final SecureRandom SECURE_RANDOM = new SecureRandom();
+
+    @Resource
+    private PoyeeAppAccountMapper poyeeAppAccountMapper;
+    @Resource
+    private PoyeeAppBaseUserMapper poyeeAppBaseUserMapper;
+
+    public AppAccountOidcServiceImpl() {
+    }
+
+    public AppAccountOidcServiceImpl(PoyeeAppAccountMapper appAccountMapper, PoyeeAppBaseUserMapper appBaseUserMapper) {
+        this.poyeeAppAccountMapper = appAccountMapper;
+        this.poyeeAppBaseUserMapper = appBaseUserMapper;
+    }
+
+    @Override
+    public EndUserDTO loadUserByLoginId(String loginId) {
+        if (!StringUtils.hasText(loginId)) {
+            return null;
+        }
+        AppAccount account = poyeeAppAccountMapper.selectByLoginId(loginId);
+        AppBaseUser baseUser;
+        if (account == null) {
+            baseUser = poyeeAppBaseUserMapper.selectByExternalLoginId(loginId);
+            if (baseUser == null) {
+                return null;
+            }
+            account = poyeeAppAccountMapper.selectByLoginId(baseUser.getUsername());
+        } else {
+            baseUser = poyeeAppBaseUserMapper.selectByUsername(account.getAccount());
+        }
+        return toEndUser(loginId, account, baseUser);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public EndUserDTO createEmailUser(EmailCreateRequest request) {
+        String email = request == null ? null : request.getEmail();
+        String displayName = request != null && request.getDisplayName() != null && request.getDisplayName().isPresent()
+                ? request.getDisplayName().get() : null;
+        return createUser(email, null, email, null, null, displayName, null, "EMAIL", null);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public EndUserDTO createEmailPasswordUser(EmailPasswordCreateRequest request) {
+        if (request == null) {
+            throw new ServiceException("参数不能为空");
+        }
+        return createUser(request.getEmail(), null, request.getEmail(), request.getPassword(), null,
+                request.getDisplayName(), null, "EMAIL_PASSWORD", null);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public EndUserDTO createPhoneUser(PhoneCreateRequest request) {
+        if (request == null || !StringUtils.hasText(request.getPhone())) {
+            throw new ServiceException("参数不能为空");
+        }
+        String phone = request.getPhone().trim();
+        return createUser(phone, phone, null, null, null, null, null, resolvePhoneRegisterChannel(request), null);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public EndUserDTO createWeChatUser(WeChatCreateRequest request) {
+        if (request == null) {
+            throw new ServiceException("参数不能为空");
+        }
+        return createUser(request.getOpenId(), null, null, null, request.getOpenId(),
+                request.getNickName(), request.getAvatarUrl(), "WECHAT_AUTH", null);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public EndUserDTO createWeChatPhoneUser(WeChatPhoneCreateRequest request) {
+        if (request == null) {
+            throw new ServiceException("参数不能为空");
+        }
+        return createUser(request.getPhone(), request.getPhone(), null, null, request.getOpenId(),
+                request.getNickName(), request.getAvatarUrl(), "WECHAT_PHONE", null);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public EndUserDTO createAppleIdUser(AppleIdCreateRequest request) {
+        if (request == null) {
+            throw new ServiceException("参数不能为空");
+        }
+        return createUser(request.getAppleId(), null, request.getEmail(), null, null,
+                request.getEmail(), request.getAvatar(), "APPLE_AUTH", request.getAppleId());
+    }
+
+    @Override
+    public void resetPassword(AccountPasswordRequest request) {
+        if (request == null || !StringUtils.hasText(request.getLoginId()) || !StringUtils.hasText(request.getNewPassword())) {
+            throw new ServiceException("参数不能为空");
+        }
+        AppAccount account = poyeeAppAccountMapper.selectByLoginId(request.getLoginId());
+        if (account == null) {
+            throw new ServiceException("账号不存在");
+        }
+        String salt = randomSalt();
+        String hash = hashPassword(request.getNewPassword(), salt);
+        poyeeAppAccountMapper.updatePassword(account.getAccount(), hash, salt);
+    }
+
+    @Override
+    public void changePassword(AccountPasswordRequest request) {
+        if (request == null || !StringUtils.hasText(request.getOldPassword())) {
+            throw new ServiceException("旧密码不能为空");
+        }
+        AppAccount account = poyeeAppAccountMapper.selectByLoginId(request.getLoginId());
+        if (account == null) {
+            throw new ServiceException("账号不存在");
+        }
+        String expected = hashPassword(request.getOldPassword(), resolvePasswordSalt(account));
+        if (!expected.equalsIgnoreCase(account.getPassword())) {
+            throw new ServiceException("旧密码错误");
+        }
+        resetPassword(request);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public EndUserDTO bindPhone(BindPhoneRequest request) {
+        if (request == null || !StringUtils.hasText(request.getLoginId()) || !StringUtils.hasText(request.getPhone())) {
+            throw new ServiceException("参数不能为空");
+        }
+        String loginId = request.getLoginId().trim();
+        String phone = request.getPhone().trim();
+        AppAccount account = poyeeAppAccountMapper.selectByLoginId(loginId);
+        if (account == null) {
+            throw new ServiceException("账号不存在");
+        }
+        AppAccount phoneAccount = poyeeAppAccountMapper.selectByLoginId(phone);
+        if (phoneAccount != null && !isSameAccount(account, phoneAccount)) {
+            throw new ServiceException("手机号已绑定");
+        }
+        poyeeAppAccountMapper.updatePhone(account.getAccount(), phone);
+        account.setPhone(phone);
+        AppBaseUser baseUser = poyeeAppBaseUserMapper.selectByUsername(account.getAccount());
+        return toEndUser(account.getAccount(), account, baseUser);
+    }
+
+    @Override
+    public AccountProfileInfoDTO getCurrentProfile(Integer userId) {
+        if (userId == null) {
+            throw new ServiceException("userId不能为空");
+        }
+        AppBaseUser baseUser = poyeeAppBaseUserMapper.selectById(userId);
+        if (baseUser == null) {
+            throw new ServiceException("用户不存在");
+        }
+        AppAccount account = StringUtils.hasText(baseUser.getUsername())
+                ? poyeeAppAccountMapper.selectByLoginId(baseUser.getUsername())
+                : null;
+
+        AccountProfileInfoDTO profile = new AccountProfileInfoDTO();
+        profile.setAvatar(baseUser.getAvatar());
+        profile.setUserId(userId.longValue());
+        profile.setNickname(baseUser.getNickname());
+        profile.setFaceVerify(baseUser.getFaceVerify());
+        profile.setIdCard(baseUser.getIdCard());
+        profile.setPhone(account == null ? null : account.getPhone());
+        return profile;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public AccountProfileInfoDTO mockRealNameVerify(Integer userId) {
+        if (userId == null) {
+            throw new ServiceException("userId cannot be null");
+        }
+        AppBaseUser baseUser = poyeeAppBaseUserMapper.selectById(userId);
+        if (baseUser == null) {
+            throw new ServiceException("user not found");
+        }
+        String idCard = randomIdCard();
+        poyeeAppBaseUserMapper.updateRealNameVerify(userId, 1, idCard);
+
+        AccountProfileInfoDTO profile = new AccountProfileInfoDTO();
+        profile.setAvatar(baseUser.getAvatar());
+        profile.setUserId(userId.longValue());
+        profile.setNickname(baseUser.getNickname());
+        profile.setFaceVerify(1);
+        profile.setIdCard(idCard);
+        return profile;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public EndUserDTO updateProfile(ProfileUpdateRequest request) {
+        if (request == null || (request.getUserId() == null && !StringUtils.hasText(request.getLoginId()))) {
+            throw new ServiceException("参数不能为空");
+        }
+        AppBaseUser baseUser = new AppBaseUser();
+        baseUser.setId(request.getUserId());
+        baseUser.setNickname(request.getNickname());
+        baseUser.setAvatar(request.getAvatar());
+        baseUser.setRealname(request.getRealname());
+        baseUser.setBirthday(request.getBirthday());
+        baseUser.setSex(request.getSex());
+        if (baseUser.getId() == null && StringUtils.hasText(request.getLoginId())) {
+            AppBaseUser existing = null;
+            AppAccount account = poyeeAppAccountMapper.selectByLoginId(request.getLoginId());
+            if (account != null) {
+                existing = poyeeAppBaseUserMapper.selectByUsername(account.getAccount());
+            }
+            if (existing == null) {
+                existing = poyeeAppBaseUserMapper.selectByExternalLoginId(request.getLoginId());
+            }
+            if (existing == null) {
+                throw new ServiceException("用户不存在");
+            }
+            baseUser.setId(existing.getId());
+        }
+        poyeeAppBaseUserMapper.updateAppBaseUser(baseUser);
+        AppBaseUser updated = poyeeAppBaseUserMapper.selectById(baseUser.getId());
+        AppAccount account = updated == null ? null : poyeeAppAccountMapper.selectByLoginId(updated.getUsername());
+        return toEndUser(updated == null ? request.getLoginId() : updated.getUsername(), account, updated);
+    }
+
+    private boolean isSameAccount(AppAccount left, AppAccount right) {
+        if (left.getId() != null && right.getId() != null) {
+            return left.getId().equals(right.getId());
+        }
+        return left.getAccount() != null && left.getAccount().equals(right.getAccount());
+    }
+
+    private String resolvePhoneRegisterChannel(PhoneCreateRequest request) {
+        String registerChannel = request.getRegisterChannel();
+        if (!StringUtils.hasText(registerChannel)) {
+            return UserType.THIRD_APK;
+        }
+        registerChannel = registerChannel.trim();
+        if (UserType.THIRD_APK.equals(registerChannel) || UserType.THIRD_APP.equals(registerChannel)) {
+            return registerChannel;
+        }
+        throw new ServiceException("手机号注册渠道不支持");
+    }
+
+    private EndUserDTO createUser(String username, String phone, String email, String password, String openId,
+                                  String displayName, String avatar, String registerChannel, String transferSubId) {
+        if (!StringUtils.hasText(username)) {
+            throw new ServiceException("账号不能为空");
+        }
+        EndUserDTO existing = loadUserByLoginId(username);
+        if (existing != null) {
+            return existing;
+        }
+
+        AppAccount account = new AppAccount();
+        account.setAccount(username);
+        account.setPhone(phone);
+        account.setEmail(email);
+        account.setStatus((short) 0);
+        account.setDelFlg((short) 0);
+        account.setRoleid(DEFAULT_ROLE_ID);
+        account.setCreateTime(new Date());
+        if (StringUtils.hasText(password)) {
+            String salt = randomSalt();
+            account.setSalt(salt);
+            account.setPassword(hashPassword(password, salt));
+        }
+        poyeeAppAccountMapper.insertAppAccount(account);
+
+        AppBaseUser baseUser = new AppBaseUser();
+        baseUser.setUsername(username);
+        baseUser.setNickname(StringUtils.hasText(displayName) ? displayName : username);
+        baseUser.setAvatar(StringUtils.hasText(avatar) ? avatar : DEFAULT_AVATAR);
+        baseUser.setOpenid(openId);
+        baseUser.setRegisterChannel(registerChannel);
+        baseUser.setStatus((short) 0);
+        baseUser.setDelFlg((short) 0);
+        baseUser.setPoint(0L);
+        baseUser.setLevel((short) 0);
+        baseUser.setCreateTime(new Date());
+        baseUser.setTransferSubId(transferSubId);
+        poyeeAppBaseUserMapper.insertAppBaseUser(baseUser);
+
+        return toEndUser(username, account, baseUser);
+    }
+
+    private EndUserDTO toEndUser(String loginId, AppAccount account, AppBaseUser baseUser) {
+        if (account == null && baseUser == null) {
+            return null;
+        }
+        EndUserDTO dto = new EndUserDTO();
+        String username = account != null ? account.getAccount() : baseUser.getUsername();
+        Integer userId = baseUser != null ? baseUser.getId() : account.getId();
+        dto.setId(userId == null ? 0 : userId);
+        dto.setUsername(username != null ? username : loginId);
+        dto.setEmail(account == null ? null : account.getEmail());
+        dto.setPhone(account == null ? null : account.getPhone());
+        dto.setDisplayName(baseUser == null ? username : baseUser.getNickname());
+        dto.setAvatarUrl(baseUser == null ? null : baseUser.getAvatar());
+        dto.setPasswordHash(account == null ? null : account.getPassword());
+        dto.setSalt(account == null ? null : account.getSalt());
+        dto.setCode(baseUser == null ? null : baseUser.getCode());
+        dto.setStatus(resolveStatus(account, baseUser));
+        dto.setIdentityType(baseUser == null ? null : baseUser.getRegisterChannel());
+        dto.setRegistrationTime(resolveRegistrationTime(account, baseUser));
+        dto.setRoleCode(resolveRoleCodes(account));
+
+        EndUserDTO.EndUserProfile profile = new EndUserDTO.EndUserProfile();
+        profile.setRealName(baseUser == null ? null : baseUser.getRealname());
+        profile.setGender(baseUser == null || baseUser.getSex() == null ? null : String.valueOf(baseUser.getSex()));
+        profile.setBirthday(baseUser == null ? null : toLocalDate(baseUser.getBirthday()));
+        profile.setIdCardNumber(baseUser == null ? null : baseUser.getIdCard());
+        profile.setAddress(baseUser == null ? null : baseUser.getRegisterAddr());
+        dto.setProfile(profile);
+        return dto;
+    }
+
+    private LocalDateTime resolveRegistrationTime(AppAccount account, AppBaseUser baseUser) {
+        Date createTime = baseUser != null && baseUser.getCreateTime() != null
+                ? baseUser.getCreateTime()
+                : account == null ? null : account.getCreateTime();
+        return toLocalDateTime(createTime);
+    }
+
+    private LocalDateTime toLocalDateTime(Date date) {
+        return date == null ? null : LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
+    }
+
+    private LocalDate toLocalDate(Date date) {
+        return date == null ? null : date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+    }
+
+    private List<String> resolveRoleCodes(AppAccount account) {
+        if (account == null || account.getRoleid() == null) {
+            return Collections.singletonList("PERSONAL");
+        }
+        List<String> roles = poyeeAppAccountMapper.selectRoleCodesByRoleId(account.getRoleid());
+        return roles == null || roles.isEmpty() ? Collections.singletonList("PERSONAL") : roles;
+    }
+
+    private String resolveStatus(AppAccount account, AppBaseUser baseUser) {
+        if (isDeleted(account, baseUser)) {
+            return INACTIVE;
+        }
+        Short accountStatus = account == null ? null : account.getStatus();
+        Short userStatus = baseUser == null ? null : baseUser.getStatus();
+        if (Short.valueOf((short) 2).equals(accountStatus) || Short.valueOf((short) 2).equals(userStatus)) {
+            return BANNED;
+        }
+        return ACTIVE;
+    }
+
+    private boolean isDeleted(AppAccount account, AppBaseUser baseUser) {
+        return (account != null && Short.valueOf((short) 1).equals(account.getDelFlg()))
+                || (baseUser != null && Short.valueOf((short) 1).equals(baseUser.getDelFlg()));
+    }
+
+    private String stableSalt(String username) {
+        return md5("poyee-account:" + (username == null ? "" : username));
+    }
+
+    private String resolvePasswordSalt(AppAccount account) {
+        return StringUtils.hasText(account.getSalt()) ? account.getSalt() : stableSalt(account.getAccount());
+    }
+
+    private String randomSalt() {
+        byte[] bytes = new byte[16];
+        SECURE_RANDOM.nextBytes(bytes);
+        StringBuilder builder = new StringBuilder();
+        for (byte b : bytes) {
+            builder.append(String.format("%02x", b));
+        }
+        return builder.toString();
+    }
+
+    private String hashPassword(String password, String salt) {
+        return md5(password + salt);
+    }
+
+    private String randomIdCard() {
+        StringBuilder builder = new StringBuilder(18);
+        for (int i = 0; i < 18; i++) {
+            builder.append(SECURE_RANDOM.nextInt(10));
+        }
+        return builder.toString();
+    }
+
+    private String md5(String value) {
+        try {
+            MessageDigest digest = MessageDigest.getInstance("MD5");
+            byte[] bytes = digest.digest(value.getBytes());
+            StringBuilder builder = new StringBuilder();
+            for (byte b : bytes) {
+                builder.append(String.format("%02x", b));
+            }
+            return builder.toString();
+        } catch (NoSuchAlgorithmException e) {
+            throw new ServiceException("密码加密失败");
+        }
+    }
+}

+ 107 - 0
poyee-account/src/main/resources/mapper/AppAccountMapper.xml

@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.tzy.mapper.PoyeeAppAccountMapper">
+
+    <resultMap id="AppAccountResult" type="com.tzy.entity.AppAccount">
+        <id property="id" column="id"/>
+        <result property="appid" column="appid"/>
+        <result property="account" column="account"/>
+        <result property="password" column="password"/>
+        <result property="email" column="email"/>
+        <result property="phone" column="phone"/>
+        <result property="remark" column="remark"/>
+        <result property="status" column="status"/>
+        <result property="roleid" column="roleid"/>
+        <result property="createBy" column="create_by"/>
+        <result property="createTime" column="create_time"/>
+        <result property="updateBy" column="update_by"/>
+        <result property="cupdateTime" column="cupdate_time"/>
+        <result property="delFlg" column="del_flg"/>
+        <result property="logOffTime" column="log_off_time"/>
+        <result property="diallingCode" column="dialling_code"/>
+        <result property="salt" column="salt"/>
+    </resultMap>
+
+    <sql id="AccountColumns">
+        id, appid, account, password, email, phone, remark, status, roleid,
+        create_by, create_time, update_by, cupdate_time, del_flg, log_off_time, dialling_code, salt
+    </sql>
+
+    <select id="selectByLoginId" resultMap="AppAccountResult">
+        select
+        <include refid="AccountColumns"/>
+        from app_account
+        where del_flg = 0
+          and (
+            account = #{loginId}
+            or phone = #{loginId}
+            or email = #{loginId}
+          )
+        order by id
+        limit 1
+    </select>
+
+    <insert id="insertAppAccount" parameterType="com.tzy.entity.AppAccount" keyColumn="id" keyProperty="id" useGeneratedKeys="true">
+        insert into app_account
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="appid != null">appid,</if>
+            <if test="account != null">account,</if>
+            <if test="password != null">password,</if>
+            <if test="email != null">email,</if>
+            <if test="phone != null">phone,</if>
+            <if test="remark != null">remark,</if>
+            <if test="status != null">status,</if>
+            <if test="roleid != null">roleid,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="cupdateTime != null">cupdate_time,</if>
+            <if test="delFlg != null">del_flg,</if>
+            <if test="logOffTime != null">log_off_time,</if>
+            <if test="diallingCode != null">dialling_code,</if>
+            <if test="salt != null">salt,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="appid != null">#{appid},</if>
+            <if test="account != null">#{account},</if>
+            <if test="password != null">#{password},</if>
+            <if test="email != null">#{email},</if>
+            <if test="phone != null">#{phone},</if>
+            <if test="remark != null">#{remark},</if>
+            <if test="status != null">#{status},</if>
+            <if test="roleid != null">#{roleid},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="cupdateTime != null">#{cupdateTime},</if>
+            <if test="delFlg != null">#{delFlg},</if>
+            <if test="logOffTime != null">#{logOffTime},</if>
+            <if test="diallingCode != null">#{diallingCode},</if>
+            <if test="salt != null">#{salt},</if>
+        </trim>
+    </insert>
+
+    <update id="updatePassword">
+        update app_account
+        set password = #{password},
+            salt = #{salt},
+            cupdate_time = now()
+        where account = #{account}
+          and del_flg = 0
+    </update>
+
+    <update id="updatePhone">
+        update app_account
+        set phone = #{phone},
+            cupdate_time = now()
+        where account = #{account}
+          and del_flg = 0
+    </update>
+
+    <select id="selectRoleCodesByRoleId" resultType="string">
+        select code
+        from app_role
+        where id = #{roleid}
+        limit 1
+    </select>
+</mapper>

+ 205 - 0
poyee-account/src/main/resources/mapper/AppBaseUserMapper.xml

@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.tzy.mapper.PoyeeAppBaseUserMapper">
+
+    <resultMap id="AppBaseUserResult" type="com.tzy.entity.AppBaseUser">
+        <id property="id" column="id"/>
+        <result property="appid" column="appid"/>
+        <result property="realname" column="realname"/>
+        <result property="nickname" column="nickname"/>
+        <result property="avatar" column="avatar"/>
+        <result property="point" column="point"/>
+        <result property="level" column="level"/>
+        <result property="birthday" column="birthday"/>
+        <result property="sex" column="sex"/>
+        <result property="openid" column="openid"/>
+        <result property="unionid" column="unionid"/>
+        <result property="registerChannel" column="register_channel"/>
+        <result property="status" column="status"/>
+        <result property="delFlg" column="del_flg"/>
+        <result property="remark" column="remark"/>
+        <result property="createBy" column="create_by"/>
+        <result property="createTime" column="create_time"/>
+        <result property="updateBy" column="update_by"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="username" column="username"/>
+        <result property="growthNum" column="growth_num"/>
+        <result property="code" column="code"/>
+        <result property="notifyFlag" column="notify_flag"/>
+        <result property="smsRegisterId" column="sms_register_id"/>
+        <result property="notifyType" column="notify_type"/>
+        <result property="faceVerify" column="face_verify"/>
+        <result property="openPsd" column="open_psd"/>
+        <result property="payPsd" column="pay_psd"/>
+        <result property="loginPsd" column="login_psd"/>
+        <result property="refusePickUp" column="refuse_pick_up"/>
+        <result property="windowOpen" column="window_open"/>
+        <result property="certName" column="cert_name"/>
+        <result property="alipayAccount" column="alipay_account"/>
+        <result property="openInvoice" column="open_invoice"/>
+        <result property="blacklist" column="blacklist"/>
+        <result property="idCard" column="id_card"/>
+        <result property="memberLevel" column="member_level"/>
+        <result property="memberName" column="member_name"/>
+        <result property="currentMonthGrowth" column="current_month_growth"/>
+        <result property="memberInitFlag" column="member_init_flag"/>
+        <result property="memberKeepGrowth" column="member_keep_growth"/>
+        <result property="registerIpAddr" column="register_ip_addr"/>
+        <result property="registerAddr" column="register_addr"/>
+        <result property="loginIpAddr" column="login_ip_addr"/>
+        <result property="loginAddr" column="login_addr"/>
+        <result property="userCertData" column="user_cert_data"/>
+        <result property="notifyTopShow" column="notify_top_show"/>
+        <result property="voiceReminder" column="voice_reminder"/>
+        <result property="vibrateReminder" column="vibrate_reminder"/>
+        <result property="consumeAmount" column="consume_amount"/>
+        <result property="orderTotalNum" column="order_total_num"/>
+        <result property="openCardShow" column="open_card_show"/>
+        <result property="effectsType" column="effects_type"/>
+        <result property="liveConfigJson" column="live_config_json"/>
+        <result property="transferSubId" column="transfer_sub_id"/>
+        <result property="cancelVerifyNum" column="cancel_verify_num"/>
+        <result property="version" column="version"/>
+        <result property="dailyLimit" column="daily_limit"/>
+        <result property="weeklyLimit" column="weekly_limit"/>
+        <result property="monthlyLimit" column="monthly_limit"/>
+        <result property="liveAnonymous" column="live_anonymous"/>
+    </resultMap>
+
+    <sql id="BaseUserColumns">
+        id, appid, realname, nickname, avatar, point, level, birthday, sex, openid, unionid,
+        register_channel, status, del_flg, remark, create_by, create_time, update_by,
+        update_time, username, growth_num, code, notify_flag, sms_register_id, notify_type,
+        face_verify, open_psd, pay_psd, login_psd, refuse_pick_up, window_open, cert_name,
+        alipay_account, open_invoice, blacklist, id_card, member_level, member_name,
+        current_month_growth, member_init_flag, member_keep_growth, register_ip_addr,
+        register_addr, login_ip_addr, login_addr, user_cert_data, notify_top_show,
+        voice_reminder, vibrate_reminder, consume_amount, order_total_num, open_card_show,
+        effects_type, live_config_json, transfer_sub_id, cancel_verify_num, version,
+        daily_limit, weekly_limit, monthly_limit, live_anonymous
+    </sql>
+
+    <select id="selectById" resultMap="AppBaseUserResult">
+        select
+        <include refid="BaseUserColumns"/>
+        from app_base_user
+        where id = #{id}
+        limit 1
+    </select>
+
+    <select id="selectByUsername" resultMap="AppBaseUserResult">
+        select
+        <include refid="BaseUserColumns"/>
+        from app_base_user
+        where username = #{username}
+        order by id
+        limit 1
+    </select>
+
+    <select id="selectByExternalLoginId" resultMap="AppBaseUserResult">
+        select
+        <include refid="BaseUserColumns"/>
+        from app_base_user
+        where username = #{loginId}
+           or openid = #{loginId}
+           or unionid = #{loginId}
+           or transfer_sub_id = #{loginId}
+        order by id
+        limit 1
+    </select>
+
+    <insert id="insertAppBaseUser" parameterType="com.tzy.entity.AppBaseUser" keyColumn="id" keyProperty="id" useGeneratedKeys="true">
+        insert into app_base_user
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="appid != null">appid,</if>
+            <if test="realname != null">realname,</if>
+            <if test="nickname != null">nickname,</if>
+            <if test="avatar != null">avatar,</if>
+            <if test="point != null">point,</if>
+            <if test="level != null">level,</if>
+            <if test="birthday != null">birthday,</if>
+            <if test="sex != null">sex,</if>
+            <if test="openid != null">openid,</if>
+            <if test="unionid != null">unionid,</if>
+            <if test="registerChannel != null">register_channel,</if>
+            <if test="status != null">status,</if>
+            <if test="delFlg != null">del_flg,</if>
+            <if test="remark != null">remark,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="username != null">username,</if>
+            <if test="growthNum != null">growth_num,</if>
+            <if test="code != null">code,</if>
+            <if test="notifyFlag != null">notify_flag,</if>
+            <if test="smsRegisterId != null">sms_register_id,</if>
+            <if test="notifyType != null">notify_type,</if>
+            <if test="faceVerify != null">face_verify,</if>
+            <if test="openPsd != null">open_psd,</if>
+            <if test="payPsd != null">pay_psd,</if>
+            <if test="loginPsd != null">login_psd,</if>
+            <if test="transferSubId != null">transfer_sub_id,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="appid != null">#{appid},</if>
+            <if test="realname != null">#{realname},</if>
+            <if test="nickname != null">#{nickname},</if>
+            <if test="avatar != null">#{avatar},</if>
+            <if test="point != null">#{point},</if>
+            <if test="level != null">#{level},</if>
+            <if test="birthday != null">#{birthday},</if>
+            <if test="sex != null">#{sex},</if>
+            <if test="openid != null">#{openid},</if>
+            <if test="unionid != null">#{unionid},</if>
+            <if test="registerChannel != null">#{registerChannel},</if>
+            <if test="status != null">#{status},</if>
+            <if test="delFlg != null">#{delFlg},</if>
+            <if test="remark != null">#{remark},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="username != null">#{username},</if>
+            <if test="growthNum != null">#{growthNum},</if>
+            <if test="code != null">#{code},</if>
+            <if test="notifyFlag != null">#{notifyFlag},</if>
+            <if test="smsRegisterId != null">#{smsRegisterId},</if>
+            <if test="notifyType != null">#{notifyType},</if>
+            <if test="faceVerify != null">#{faceVerify},</if>
+            <if test="openPsd != null">#{openPsd},</if>
+            <if test="payPsd != null">#{payPsd},</if>
+            <if test="loginPsd != null">#{loginPsd},</if>
+            <if test="transferSubId != null">#{transferSubId},</if>
+        </trim>
+    </insert>
+
+    <update id="updateAppBaseUser" parameterType="com.tzy.entity.AppBaseUser">
+        update app_base_user
+        <trim prefix="set" suffixOverrides=",">
+            <if test="realname != null">realname = #{realname},</if>
+            <if test="nickname != null">nickname = #{nickname},</if>
+            <if test="avatar != null">avatar = #{avatar},</if>
+            <if test="birthday != null">birthday = #{birthday},</if>
+            <if test="sex != null">sex = #{sex},</if>
+            <if test="openid != null">openid = #{openid},</if>
+            <if test="unionid != null">unionid = #{unionid},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="delFlg != null">del_flg = #{delFlg},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            update_time = now(),
+            <if test="smsRegisterId != null">sms_register_id = #{smsRegisterId},</if>
+            <if test="transferSubId != null">transfer_sub_id = #{transferSubId},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <update id="updateRealNameVerify">
+        update app_base_user
+        set face_verify = #{faceVerify},
+            id_card = #{idCard},
+            update_time = now()
+        where id = #{userId}
+    </update>
+</mapper>

+ 12 - 0
poyee-order/src/main/java/com/tzy/service/impl/LotOrderServiceImpl.java

@@ -1,8 +1,10 @@
 package com.tzy.service.impl;
 package com.tzy.service.impl;
 
 
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageHelper;
+import com.tzy.common.dto.UserInfo;
 import com.tzy.common.exception.ServiceException;
 import com.tzy.common.exception.ServiceException;
 import com.tzy.common.utils.DateUtils;
 import com.tzy.common.utils.DateUtils;
+import com.tzy.common.utils.UserUtils;
 import com.tzy.dto.LogisticsDTO;
 import com.tzy.dto.LogisticsDTO;
 import com.tzy.entity.LotOrder;
 import com.tzy.entity.LotOrder;
 import com.tzy.entity.Spu;
 import com.tzy.entity.Spu;
@@ -37,6 +39,11 @@ public class LotOrderServiceImpl implements LotOrderService {
     LotOrderExpressMapper lotOrderExpressMapper;
     LotOrderExpressMapper lotOrderExpressMapper;
     @Override
     @Override
     public List<LotOrder> getLotOrders(LotOrder lotOrder) {
     public List<LotOrder> getLotOrders(LotOrder lotOrder) {
+        UserInfo userInfo = UserUtils.getSimpleUserInfo();
+        if (userInfo == null) {
+            throw new ServiceException(500, "请先登录");
+        }
+        lotOrder.setUserId(userInfo.getId().longValue());
         PageHelper.startPage(lotOrder.getPageNum(), lotOrder.getPageSize());
         PageHelper.startPage(lotOrder.getPageNum(), lotOrder.getPageSize());
         List<LotOrder> lotOrders = lotOrderMapper.selectLotOrder(lotOrder);
         List<LotOrder> lotOrders = lotOrderMapper.selectLotOrder(lotOrder);
         fillTimestamp(lotOrders);
         fillTimestamp(lotOrders);
@@ -46,6 +53,10 @@ public class LotOrderServiceImpl implements LotOrderService {
     @Override
     @Override
     @Transactional(rollbackFor = Exception.class)
     @Transactional(rollbackFor = Exception.class)
     public int createLotOrder(LotOrder lotOrder) {
     public int createLotOrder(LotOrder lotOrder) {
+        UserInfo userInfo = UserUtils.getSimpleUserInfo();
+        if (userInfo == null) {
+            throw new ServiceException(500, "请先登录");
+        }
         //判断该拍品订单是否已存在
         //判断该拍品订单是否已存在
         int countedLotOrderByLotId = lotOrderMapper.countLotOrderByLotId(lotOrder);
         int countedLotOrderByLotId = lotOrderMapper.countLotOrderByLotId(lotOrder);
         if (countedLotOrderByLotId > 0) {
         if (countedLotOrderByLotId > 0) {
@@ -59,6 +70,7 @@ public class LotOrderServiceImpl implements LotOrderService {
 
 
         lotOrder.setCreateTime(DateUtils.getNowDate());
         lotOrder.setCreateTime(DateUtils.getNowDate());
         lotOrder.setStatus(100);//待支付
         lotOrder.setStatus(100);//待支付
+        lotOrder.setUserId(userInfo.getId().longValue());
         lotOrder.setOrderNo(RandomUtil.getRandom(RandomUtil.MALL_USER_ORDER));
         lotOrder.setOrderNo(RandomUtil.getRandom(RandomUtil.MALL_USER_ORDER));
         return lotOrderMapper.insertLotOrder(lotOrder);
         return lotOrderMapper.insertLotOrder(lotOrder);
     }
     }

+ 5 - 0
poyi-app/pom.xml

@@ -22,6 +22,11 @@
             <artifactId>poyee-order</artifactId>
             <artifactId>poyee-order</artifactId>
             <version>${tzy.version}</version>
             <version>${tzy.version}</version>
         </dependency>
         </dependency>
+        <dependency>
+            <groupId>com.tzy</groupId>
+            <artifactId>poyee-account</artifactId>
+            <version>${tzy.version}</version>
+        </dependency>
         <dependency>
         <dependency>
             <groupId>org.codehaus.janino</groupId>
             <groupId>org.codehaus.janino</groupId>
             <artifactId>janino</artifactId>
             <artifactId>janino</artifactId>