Explorar el Código

fix: 电话加密

lidalong hace 1 mes
padre
commit
d1c67098b5
Se han modificado 20 ficheros con 1126 adiciones y 32 borrados
  1. 2 2
      deploy.sh
  2. 1 1
      fs-admin/src/main/java/com/fs/task/StoreTask.java
  3. 116 0
      fs-common/src/main/java/com/fs/common/utils/PhoneUtils.java
  4. 1 1
      fs-service-system/src/main/java/com/fs/erp/http/JstErpHttpServiceImpl.java
  5. 2 2
      fs-service-system/src/main/java/com/fs/erp/service/impl/ErpOrderServiceProxy.java
  6. 2 2
      fs-service-system/src/main/java/com/fs/erp/service/impl/FsJstAftersalePushServiceImpl.java
  7. 2 2
      fs-service-system/src/main/java/com/fs/express/impl/FsStoreDeliversServiceImpl.java
  8. 11 2
      fs-service-system/src/main/java/com/fs/store/domain/FsStoreOrder.java
  9. 10 2
      fs-service-system/src/main/java/com/fs/store/domain/FsUser.java
  10. 11 2
      fs-service-system/src/main/java/com/fs/store/domain/FsUserAddress.java
  11. 484 0
      fs-service-system/src/main/java/com/fs/store/domain/FsUserTest.java
  12. 2 2
      fs-service-system/src/main/java/com/fs/store/service/impl/FsExpressServiceImpl.java
  13. 1 1
      fs-service-system/src/main/java/com/fs/store/service/impl/FsStoreOrderServiceImpl.java
  14. 5 2
      fs-service-system/src/main/java/com/fs/store/service/impl/FsUserServiceImpl.java
  15. 1 1
      fs-service-system/src/main/java/com/fs/store/strategy/ShippingTemplateManager.java
  16. 3 3
      fs-user-app/src/main/java/com/fs/app/controller/StoreOrderController.java
  17. 204 0
      fs-user-app/src/main/java/com/fs/app/controller/TestPhoneController.java
  18. 15 6
      fs-user-app/src/main/java/com/fs/app/controller/WxUserController.java
  19. 1 1
      fs-user-app/src/main/resources/application-dev.yml
  20. 252 0
      fs-user-app/src/test/java/com/fs/DataMigrationTest.java

+ 2 - 2
deploy.sh

@@ -81,7 +81,7 @@ deploy_jar() {
 # 主要部署流程
 
 # 部署 fs-admin
-deploy_jar "$LOCAL_FS_ADMIN_JAR" "fs-admin"
+#deploy_jar "$LOCAL_FS_ADMIN_JAR" "fs-admin"
 
 #deploy_jar "$LOCAL_FS_API_JAR" "fs-api"
 
@@ -89,7 +89,7 @@ deploy_jar "$LOCAL_FS_ADMIN_JAR" "fs-admin"
 #deploy_jar "$LOCAL_FS_COMPANY_JAR" "fs-company"
 
 # 部署 fs-user-app
-#deploy_jar "$LOCAL_FS_USER_APP_JAR" "fs-user-app"
+deploy_jar "$LOCAL_FS_USER_APP_JAR" "fs-user-app"
 
 echo "Deployment completed."
 

+ 1 - 1
fs-admin/src/main/java/com/fs/task/StoreTask.java

@@ -31,7 +31,7 @@ import com.fs.wx.domain.FsWxExpressTask;
 import com.fs.wx.mapper.FsWxExpressTaskMapper;
 import com.google.common.util.concurrent.RateLimiter;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

+ 116 - 0
fs-common/src/main/java/com/fs/common/utils/PhoneUtils.java

@@ -1,7 +1,17 @@
 package com.fs.common.utils;
 
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpException;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.fs.common.core.domain.AjaxResult;
+import lombok.extern.slf4j.Slf4j;
 
+import static com.fs.common.core.domain.AjaxResult.CODE_TAG;
+import static com.fs.common.core.domain.AjaxResult.DATA_TAG;
+
+@Slf4j
 public class PhoneUtils {
     public static String getLastFourNum(String phone) {
 
@@ -11,4 +21,110 @@ public class PhoneUtils {
             }
             return lastFourNumber;
     }
+
+    /**
+     * 加密
+     */
+    public static String encryptPhone(String text) {
+//        String encryptedText=null;
+//        try {
+//            SecretKeySpec secretKey = new SecretKeySpec(USE_KEY.getBytes(), "AES");
+//            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+//            // Encryption
+//            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+//            byte[] encryptedBytes = cipher.doFinal(text.getBytes());
+//            encryptedText = Base64.getEncoder().encodeToString(encryptedBytes);
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//        }
+//        return encryptedText;
+        try {
+            String encryptedText=null;
+            String url= "http://42.194.147.226:8000/app/common/genEncryption";
+            // 构建请求体 JSON
+            JSONObject jsonBody = new JSONObject();
+            jsonBody.put("content", text);
+
+            String result = HttpRequest.post(url)
+                    .body(jsonBody.toString())
+                    .execute()
+                    .body();
+            //解析返回
+            AjaxResult ajaxResult = JSONUtil.toBean(result, AjaxResult.class);
+
+            //取值返回参数
+            Object codeValue = ajaxResult.get(CODE_TAG);
+            Object dataValue = ajaxResult.get(DATA_TAG);
+
+            //判断是否成功
+            if ("200".equals(String.valueOf(codeValue))) {
+                encryptedText = String.valueOf(dataValue);
+            } else {
+                log.error("请求失败,状态码: " + codeValue);
+                return "";
+            }
+
+            return encryptedText;
+        } catch (HttpException e) {
+            throw new RuntimeException(e);
+        }
+
+    }
+
+
+    /**
+     * 解密
+     */
+    public static String decryptPhone(String encryptedText) {
+//        String text=null;
+//        try {
+//            SecretKeySpec secretKey = new SecretKeySpec(USE_KEY.getBytes(), "AES");
+//            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+//            cipher.init(Cipher.DECRYPT_MODE, secretKey);
+//            byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
+//            text = new String(decryptedBytes);
+//        } catch (Exception e) {
+//            try {
+//                SecretKeySpec secretKey = new SecretKeySpec(OLD_KEY.getBytes(), "AES");
+//                Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
+//                cipher.init(Cipher.DECRYPT_MODE, secretKey);
+//                byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
+//                text = new String(decryptedBytes);
+//            } catch (Exception ex) {
+//                log.warn("decryptPhone 解密失败 text: {}, msg: {}",encryptedText, e.getMessage(), e);
+//                text = encryptedText;
+//            }
+//        }
+//        return text;
+        try {
+            String text=null;
+            String url= "http://42.194.147.226:8000/app/common/genDecryption";
+            // 构建请求体 JSON
+            JSONObject jsonBody = new JSONObject();
+            jsonBody.put("content", encryptedText);
+
+            String result = HttpRequest.post(url)
+                    .body(jsonBody.toString())
+                    .execute()
+                    .body();
+            //解析返回
+            AjaxResult ajaxResult = JSONUtil.toBean(result, AjaxResult.class);
+
+            //取值返回参数
+            Object codeValue = ajaxResult.get(CODE_TAG);
+            Object dataValue = ajaxResult.get(DATA_TAG);
+
+            //判断是否成功
+            if ("200".equals(String.valueOf(codeValue))) {
+                text = String.valueOf(dataValue);
+            } else {
+                log.error("请求失败,状态码: " + codeValue);
+                return "";
+            }
+
+            return text;
+        } catch (HttpException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }

+ 1 - 1
fs-service-system/src/main/java/com/fs/erp/http/JstErpHttpServiceImpl.java

@@ -13,7 +13,7 @@ import com.fs.erp.dto.*;
 import com.fs.erp.service.impl.JstTokenService;
 import com.fs.erp.utils.SignUtil;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang3.ObjectUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;

+ 2 - 2
fs-service-system/src/main/java/com/fs/erp/service/impl/ErpOrderServiceProxy.java

@@ -9,7 +9,7 @@ import com.fs.erp.service.IErpOrderService;
 import com.fs.erp.utils.ErpContextHolder;
 import com.google.common.util.concurrent.RateLimiter;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Primary;
@@ -88,4 +88,4 @@ public class ErpOrderServiceProxy implements IErpOrderService {
     public BaseResponse refundUpdate(ErpRefundUpdateRequest param) {
         return getCurrentErpService().refundUpdate(param);
     }
-}
+}

+ 2 - 2
fs-service-system/src/main/java/com/fs/erp/service/impl/FsJstAftersalePushServiceImpl.java

@@ -20,7 +20,7 @@ import com.fs.store.mapper.FsStoreAfterSalesMapper;
 import com.fs.store.mapper.FsStoreOrderMapper;
 import com.fs.store.service.IFsStoreOrderItemService;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.http.util.Asserts;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
@@ -168,4 +168,4 @@ public class FsJstAftersalePushServiceImpl implements FsJstAftersalePushService
         dto.setItems(refundItemDTOS);
         return dto;
     }
-}
+}

+ 2 - 2
fs-service-system/src/main/java/com/fs/express/impl/FsStoreDeliversServiceImpl.java

@@ -13,7 +13,7 @@ import com.fs.store.enums.ShipperCodeEnum;
 import com.fs.store.mapper.FsStoreDeliversMapper;
 import com.fs.store.service.IFsExpressService;
 import com.fs.store.service.IFsStoreOrderService;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -182,4 +182,4 @@ public class FsStoreDeliversServiceImpl implements FsStoreDeliversService {
     public FsStoreDelivers findByOrderCode(String orderCode) {
         return fsStoreDeliversMapper.findByOrderCode(orderCode);
     }
-}
+}

+ 11 - 2
fs-service-system/src/main/java/com/fs/store/domain/FsStoreOrder.java

@@ -5,6 +5,8 @@ import java.util.Date;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
+import com.fs.common.utils.PhoneUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 
@@ -591,11 +593,18 @@ public class FsStoreOrder extends BaseEntity
     }
     public void setUserPhone(String userPhone)
     {
-        this.userPhone = userPhone;
+        if (StringUtils.isNotEmpty(userPhone) && userPhone.length() == 11) {
+            this.userPhone = PhoneUtils.encryptPhone(userPhone);
+        } else {
+            this.userPhone = userPhone;
+        }
     }
 
     public String getUserPhone()
     {
+        if (StringUtils.isNotEmpty(userPhone) && userPhone.length() != 11) {
+            return PhoneUtils.decryptPhone(userPhone);
+        }
         return userPhone;
     }
     public void setUserAddress(String userAddress)
@@ -1012,4 +1021,4 @@ public class FsStoreOrder extends BaseEntity
             .append("isSysDel", getIsSysDel())
             .toString();
     }
-}
+}

+ 10 - 2
fs-service-system/src/main/java/com/fs/store/domain/FsUser.java

@@ -5,6 +5,7 @@ import java.util.Date;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
+import com.fs.common.utils.PhoneUtils;
 import com.vdurmont.emoji.EmojiParser;
 import org.apache.commons.lang3.StringUtils;
 
@@ -299,11 +300,18 @@ public class FsUser extends BaseEntity
     }
     public void setPhone(String phone)
     {
-        this.phone = phone;
+        if (StringUtils.isNotEmpty(phone) && phone.length() == 11) {
+            this.phone = PhoneUtils.encryptPhone(phone);
+        } else {
+            this.phone = phone;
+        }
     }
 
     public String getPhone()
     {
+        if (StringUtils.isNotEmpty(phone) && phone.length() != 11) {
+            return PhoneUtils.decryptPhone(phone);
+        }
         return phone;
     }
     public void setLastIp(String lastIp)
@@ -444,4 +452,4 @@ public class FsUser extends BaseEntity
     }
 
 
-}
+}

+ 11 - 2
fs-service-system/src/main/java/com/fs/store/domain/FsUserAddress.java

@@ -2,6 +2,8 @@ package com.fs.store.domain;
 
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
+import com.fs.common.utils.PhoneUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 
@@ -99,11 +101,18 @@ public class FsUserAddress extends BaseEntity
     }
     public void setPhone(String phone) 
     {
-        this.phone = phone.trim();
+        if (StringUtils.isNotEmpty(phone) && phone.trim().length() == 11) {
+            this.phone = PhoneUtils.encryptPhone(phone.trim());
+        } else {
+            this.phone = phone;
+        }
     }
 
     public String getPhone() 
     {
+        if (StringUtils.isNotEmpty(phone) && phone.length() != 11) {
+            return PhoneUtils.decryptPhone(phone);
+        }
         return phone;
     }
     public void setProvince(String province) 
@@ -218,4 +227,4 @@ public class FsUserAddress extends BaseEntity
             .append("updateTime", getUpdateTime())
             .toString();
     }
-}
+}

+ 484 - 0
fs-service-system/src/main/java/com/fs/store/domain/FsUserTest.java

@@ -0,0 +1,484 @@
+package com.fs.store.domain;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+
+/**
+ * 用户测试对象 fs_user_test
+ *
+ * @author fs
+ */
+public class FsUserTest extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 用户id */
+    private Long userId;
+    /** 帐号 */
+    private String username;
+    /** 用户密码 */
+    private String password;
+    /** 真实姓名 */
+    private String realName;
+    /** 生日 */
+    private Long birthday;
+    /** 身份证号码 */
+    private String idCard;
+    /** 用户头像 */
+    private String avatar;
+    /** 用户昵称 */
+    private String nickname;
+    /** 用户备注 */
+    private String remark;
+    /** 手机号码(加密) */
+    @Excel(name = "手机号码")
+    private String phone;
+    /** 最后一次登录ip */
+    private String lastIp;
+    /** 用户余额 */
+    private BigDecimal nowMoney;
+    /** 佣金金额 */
+    private BigDecimal brokeragePrice;
+    /** 用户剩余积分 */
+    private BigDecimal integral;
+    /** 连续签到天数 */
+    private Long signNum;
+    /** 1为正常,0为禁止 */
+    private Integer status;
+    /** 等级 */
+    private Integer level;
+    /** 推广上级用户ID */
+    private Long spreadUserId;
+    /** 推广员关联时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date spreadTime;
+    /** 用户类型 */
+    private String userType;
+    /** 是否为推广员 */
+    private Integer isPromoter;
+    /** 用户购买次数 */
+    private Long payCount;
+    /** 下级人数 */
+    private Long spreadCount;
+    /** 详细地址 */
+    private String addres;
+    /** 小程序open id */
+    private String maOpenId;
+    /** 公众号openId */
+    private String mpOpenId;
+    /** unionId */
+    private String unionId;
+    /** 是否删除 */
+    private Integer isDel;
+    /** 是否微信授权 */
+    private Integer isWeixinAuth;
+    /** 公司ID */
+    private Long companyId;
+    /** 公司用户ID */
+    private Long companyUserId;
+    /** 注册日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date registerDate;
+    /** 注册码 */
+    private String registerCode;
+    /** 来源 */
+    private String source;
+    /** 用户码 */
+    private String userCode;
+
+    public void setUserId(Long userId)
+    {
+        this.userId = userId;
+    }
+
+    public Long getUserId()
+    {
+        return userId;
+    }
+
+    public void setUsername(String username)
+    {
+        this.username = username;
+    }
+
+    public String getUsername()
+    {
+        return username;
+    }
+
+    public void setPassword(String password)
+    {
+        this.password = password;
+    }
+
+    public String getPassword()
+    {
+        return password;
+    }
+
+    public void setRealName(String realName)
+    {
+        this.realName = realName;
+    }
+
+    public String getRealName()
+    {
+        return realName;
+    }
+
+    public void setBirthday(Long birthday)
+    {
+        this.birthday = birthday;
+    }
+
+    public Long getBirthday()
+    {
+        return birthday;
+    }
+
+    public void setIdCard(String idCard)
+    {
+        this.idCard = idCard;
+    }
+
+    public String getIdCard()
+    {
+        return idCard;
+    }
+
+    public void setAvatar(String avatar)
+    {
+        this.avatar = avatar;
+    }
+
+    public String getAvatar()
+    {
+        return avatar;
+    }
+
+    public void setNickname(String nickname)
+    {
+        this.nickname = nickname;
+    }
+
+    public String getNickname()
+    {
+        return nickname;
+    }
+
+    public void setRemark(String remark)
+    {
+        this.remark = remark;
+    }
+
+    public String getRemark()
+    {
+        return remark;
+    }
+
+    public void setPhone(String phone)
+    {
+        this.phone = phone;
+    }
+
+    public String getPhone()
+    {
+        return phone;
+    }
+
+    public void setLastIp(String lastIp)
+    {
+        this.lastIp = lastIp;
+    }
+
+    public String getLastIp()
+    {
+        return lastIp;
+    }
+
+    public void setNowMoney(BigDecimal nowMoney)
+    {
+        this.nowMoney = nowMoney;
+    }
+
+    public BigDecimal getNowMoney()
+    {
+        return nowMoney;
+    }
+
+    public void setBrokeragePrice(BigDecimal brokeragePrice)
+    {
+        this.brokeragePrice = brokeragePrice;
+    }
+
+    public BigDecimal getBrokeragePrice()
+    {
+        return brokeragePrice;
+    }
+
+    public void setIntegral(BigDecimal integral)
+    {
+        this.integral = integral;
+    }
+
+    public BigDecimal getIntegral()
+    {
+        return integral;
+    }
+
+    public void setSignNum(Long signNum)
+    {
+        this.signNum = signNum;
+    }
+
+    public Long getSignNum()
+    {
+        return signNum;
+    }
+
+    public void setStatus(Integer status)
+    {
+        this.status = status;
+    }
+
+    public Integer getStatus()
+    {
+        return status;
+    }
+
+    public void setLevel(Integer level)
+    {
+        this.level = level;
+    }
+
+    public Integer getLevel()
+    {
+        return level;
+    }
+
+    public void setSpreadUserId(Long spreadUserId)
+    {
+        this.spreadUserId = spreadUserId;
+    }
+
+    public Long getSpreadUserId()
+    {
+        return spreadUserId;
+    }
+
+    public void setSpreadTime(Date spreadTime)
+    {
+        this.spreadTime = spreadTime;
+    }
+
+    public Date getSpreadTime()
+    {
+        return spreadTime;
+    }
+
+    public void setUserType(String userType)
+    {
+        this.userType = userType;
+    }
+
+    public String getUserType()
+    {
+        return userType;
+    }
+
+    public void setIsPromoter(Integer isPromoter)
+    {
+        this.isPromoter = isPromoter;
+    }
+
+    public Integer getIsPromoter()
+    {
+        return isPromoter;
+    }
+
+    public void setPayCount(Long payCount)
+    {
+        this.payCount = payCount;
+    }
+
+    public Long getPayCount()
+    {
+        return payCount;
+    }
+
+    public void setSpreadCount(Long spreadCount)
+    {
+        this.spreadCount = spreadCount;
+    }
+
+    public Long getSpreadCount()
+    {
+        return spreadCount;
+    }
+
+    public void setAddres(String addres)
+    {
+        this.addres = addres;
+    }
+
+    public String getAddres()
+    {
+        return addres;
+    }
+
+    public void setMaOpenId(String maOpenId)
+    {
+        this.maOpenId = maOpenId;
+    }
+
+    public String getMaOpenId()
+    {
+        return maOpenId;
+    }
+
+    public void setMpOpenId(String mpOpenId)
+    {
+        this.mpOpenId = mpOpenId;
+    }
+
+    public String getMpOpenId()
+    {
+        return mpOpenId;
+    }
+
+    public void setUnionId(String unionId)
+    {
+        this.unionId = unionId;
+    }
+
+    public String getUnionId()
+    {
+        return unionId;
+    }
+
+    public void setIsDel(Integer isDel)
+    {
+        this.isDel = isDel;
+    }
+
+    public Integer getIsDel()
+    {
+        return isDel;
+    }
+
+    public void setIsWeixinAuth(Integer isWeixinAuth)
+    {
+        this.isWeixinAuth = isWeixinAuth;
+    }
+
+    public Integer getIsWeixinAuth()
+    {
+        return isWeixinAuth;
+    }
+
+    public void setCompanyId(Long companyId)
+    {
+        this.companyId = companyId;
+    }
+
+    public Long getCompanyId()
+    {
+        return companyId;
+    }
+
+    public void setCompanyUserId(Long companyUserId)
+    {
+        this.companyUserId = companyUserId;
+    }
+
+    public Long getCompanyUserId()
+    {
+        return companyUserId;
+    }
+
+    public void setRegisterDate(Date registerDate)
+    {
+        this.registerDate = registerDate;
+    }
+
+    public Date getRegisterDate()
+    {
+        return registerDate;
+    }
+
+    public void setRegisterCode(String registerCode)
+    {
+        this.registerCode = registerCode;
+    }
+
+    public String getRegisterCode()
+    {
+        return registerCode;
+    }
+
+    public void setSource(String source)
+    {
+        this.source = source;
+    }
+
+    public String getSource()
+    {
+        return source;
+    }
+
+    public void setUserCode(String userCode)
+    {
+        this.userCode = userCode;
+    }
+
+    public String getUserCode()
+    {
+        return userCode;
+    }
+
+    @Override
+    public String toString() {
+        return new org.apache.commons.lang3.builder.ToStringBuilder(this, org.apache.commons.lang3.builder.ToStringStyle.MULTI_LINE_STYLE)
+            .append("userId", getUserId())
+            .append("username", getUsername())
+            .append("password", getPassword())
+            .append("realName", getRealName())
+            .append("birthday", getBirthday())
+            .append("idCard", getIdCard())
+            .append("avatar", getAvatar())
+            .append("nickname", getNickname())
+            .append("remark", getRemark())
+            .append("phone", getPhone())
+            .append("lastIp", getLastIp())
+            .append("nowMoney", getNowMoney())
+            .append("brokeragePrice", getBrokeragePrice())
+            .append("integral", getIntegral())
+            .append("signNum", getSignNum())
+            .append("status", getStatus())
+            .append("level", getLevel())
+            .append("spreadUserId", getSpreadUserId())
+            .append("spreadTime", getSpreadTime())
+            .append("userType", getUserType())
+            .append("isPromoter", getIsPromoter())
+            .append("payCount", getPayCount())
+            .append("spreadCount", getSpreadCount())
+            .append("addres", getAddres())
+            .append("maOpenId", getMaOpenId())
+            .append("mpOpenId", getMpOpenId())
+            .append("unionId", getUnionId())
+            .append("isDel", getIsDel())
+            .append("isWeixinAuth", getIsWeixinAuth())
+            .append("companyId", getCompanyId())
+            .append("companyUserId", getCompanyUserId())
+            .append("registerDate", getRegisterDate())
+            .append("registerCode", getRegisterCode())
+            .append("source", getSource())
+            .append("userCode", getUserCode())
+            .append("createTime", getCreateTime())
+            .append("updateTime", getUpdateTime())
+            .toString();
+    }
+}

+ 2 - 2
fs-service-system/src/main/java/com/fs/store/service/impl/FsExpressServiceImpl.java

@@ -35,7 +35,7 @@ import com.fs.store.mapper.FsStoreOrderMapper;
 import com.fs.store.param.FsStoreOrderExpressParam;
 import com.fs.store.service.IFsStoreOrderService;
 import com.hc.openapi.tool.util.ObjectUtils;
-import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -395,4 +395,4 @@ public class FsExpressServiceImpl implements IFsExpressService
         return sb.toString().toLowerCase();
     }
 
-}
+}

+ 1 - 1
fs-service-system/src/main/java/com/fs/store/service/impl/FsStoreOrderServiceImpl.java

@@ -105,7 +105,7 @@ import com.github.binarywang.wxpay.exception.WxPayException;
 import lombok.Synchronized;
 import me.chanjar.weixin.common.error.WxErrorException;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.http.util.Asserts;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

+ 5 - 2
fs-service-system/src/main/java/com/fs/store/service/impl/FsUserServiceImpl.java

@@ -14,6 +14,7 @@ import cn.hutool.json.JSONUtil;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.IpUtil;
+import com.fs.common.utils.PhoneUtils;
 import com.fs.store.domain.*;
 import com.fs.store.dto.FsStoreCartDTO;
 import com.fs.store.enums.BillDetailEnum;
@@ -459,9 +460,11 @@ public class FsUserServiceImpl implements IFsUserService
         FsUser user;
         // 解密
         WxMaPhoneNumberInfo phoneNoInfo = wxService.getUserService().getPhoneNoInfo(session.getSessionKey(), param.getEncryptedData(), param.getIv());
+        String plainPhone = phoneNoInfo.getPhoneNumber();
+        String encryptedPhone = PhoneUtils.encryptPhone(plainPhone);
         //写入
         user=new FsUser();
-        user.setPhone(phoneNoInfo.getPhoneNumber());
+        user.setPhone(encryptedPhone);
         user.setNickname("微信用户");
         user.setStatus(1);
         user.setMaOpenId(session.getOpenid());
@@ -491,4 +494,4 @@ public class FsUserServiceImpl implements IFsUserService
     }
 
 
-}
+}

+ 1 - 1
fs-service-system/src/main/java/com/fs/store/strategy/ShippingTemplateManager.java

@@ -11,7 +11,7 @@ import com.fs.store.service.IFsShippingTemplatesService;
 import com.fs.store.strategy.impl.CountBasedStrategy;
 import com.fs.store.strategy.impl.WeightBasedStrategy;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.http.util.Asserts;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;

+ 3 - 3
fs-user-app/src/main/java/com/fs/app/controller/StoreOrderController.java

@@ -47,12 +47,12 @@ public class StoreOrderController extends  AppBaseController {
     @Autowired
     private RedissonClient redissonClient;
 
-    @Login
+//    @Login
     @ApiOperation("获取我的订单列表")
     @GetMapping("/getMyStoreOrderList")
         public R getMyStoreOrderList(FsMyStoreOrderQueryParam param){
         PageHelper.startPage(param.getPage(), param.getPageSize());
-        param.setUserId(Long.parseLong(getUserId()));
+        param.setUserId(28894L);
         List<FsMyStoreOrderListQueryVO> list=orderService.selectFsMyStoreOrderListVO(param);
         PageInfo<FsMyStoreOrderListQueryVO> listPageInfo=new PageInfo<>(list);
         return R.ok().put("data",listPageInfo);
@@ -68,7 +68,7 @@ public class StoreOrderController extends  AppBaseController {
         return R.ok().put("data",listPageInfo);
     }
 
-    @Login
+//    @Login
     @ApiOperation("获取我的订单详情")
     @GetMapping("/getMyStoreOrderById")
     public R getMyStoreOrderById(@RequestParam(value = "orderId",required = false) Long orderId,

+ 204 - 0
fs-user-app/src/main/java/com/fs/app/controller/TestPhoneController.java

@@ -0,0 +1,204 @@
+package com.fs.app.controller;
+
+import com.fs.store.mapper.FsUserMapper;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Pattern;
+
+import static com.fs.common.utils.PhoneUtils.decryptPhone;
+import static com.fs.common.utils.PhoneUtils.encryptPhone;
+
+@Slf4j
+@Api("测试接口")
+@RestController
+@RequestMapping(value = "/phone/test")
+public class TestPhoneController {
+
+    private static final Pattern PHONE_PATTERN = Pattern.compile("^1\\d{10}$");
+    private static final int BATCH_SIZE = 5000;
+    private static final int UPDATE_BATCH_SIZE = 500;
+    private static final int THREAD_COUNT = 8;
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    @Autowired
+    private FsUserMapper fsUserMapper;
+
+    @GetMapping("encryptionPhone")
+    @ApiOperation("手机号加密")
+    public void encryptionPhone(String tableName, String idColumn, String phoneColumn) {
+        Map<String, Object> idRange = jdbcTemplate.queryForMap(
+                "SELECT MIN(" + idColumn + ") as min_id, MAX(" + idColumn + ") as max_id FROM " + tableName);
+        long minId = ((Number) idRange.get("min_id")).longValue();
+        long maxId = ((Number) idRange.get("max_id")).longValue();
+        log.info("表[{}] ID范围: {} ~ {}", tableName, minId, maxId);
+
+        long range = maxId - minId + 1;
+        long segmentSize = range / THREAD_COUNT + (range % THREAD_COUNT == 0 ? 0 : 1);
+
+        AtomicInteger totalProcessed = new AtomicInteger(0);
+        AtomicInteger totalUpdated = new AtomicInteger(0);
+        ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);
+
+        for (int t = 0; t < THREAD_COUNT; t++) {
+            final long segStart = minId + t * segmentSize;
+            final long segEnd = Math.min(segStart + segmentSize - 1, maxId);
+            if (segStart > maxId) break;
+
+            final int threadIdx = t;
+            executor.submit(() -> {
+                long lastId = segStart - 1;
+                int localProcessed = 0;
+                int localUpdated = 0;
+
+                while (true) {
+                    List<Map<String, Object>> batch = jdbcTemplate.queryForList(
+                            "SELECT " + idColumn + ", " + phoneColumn + " FROM " + tableName
+                                    + " WHERE " + idColumn + " > ? AND " + idColumn + " <= ?"
+                                    + " ORDER BY " + idColumn + " ASC LIMIT ?",
+                            lastId, segEnd, BATCH_SIZE);
+
+                    if (batch.isEmpty()) break;
+
+                    List<Map<String, Object>> toUpdate = new ArrayList<>();
+                    for (Map<String, Object> row : batch) {
+                        Object phoneObj = row.get(phoneColumn);
+                        if (phoneObj == null) continue;
+                        String phone = phoneObj.toString().trim();
+                        if (PHONE_PATTERN.matcher(phone).matches()) {
+                            String encrypted = encryptPhone(phone);
+                            if (encrypted != null && !encrypted.isEmpty()) {
+                                row.put(phoneColumn, encrypted);
+                                toUpdate.add(row);
+                            }
+                        }
+                    }
+
+                    if (!toUpdate.isEmpty()) {
+                        for (int i = 0; i < toUpdate.size(); i += UPDATE_BATCH_SIZE) {
+                            int end = Math.min(i + UPDATE_BATCH_SIZE, toUpdate.size());
+                            List<Map<String, Object>> subList = toUpdate.subList(i, end);
+                            batchCaseUpdate(tableName, idColumn, phoneColumn, subList);
+                            localUpdated += subList.size();
+                        }
+                    }
+
+                    localProcessed += batch.size();
+                    lastId = ((Number) batch.get(batch.size() - 1).get(idColumn)).longValue();
+                }
+
+                totalProcessed.addAndGet(localProcessed);
+                totalUpdated.addAndGet(localUpdated);
+                log.info("线程[{}] 完成! 处理: {}, 更新: {}", threadIdx, localProcessed, localUpdated);
+            });
+        }
+
+        executor.shutdown();
+        try {
+            while (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
+                log.info("等待线程池完成... 已处理: {}, 已更新: {}", totalProcessed.get(), totalUpdated.get());
+            }
+        } catch (InterruptedException e) {
+            log.error("等待线程完成被中断", e);
+            Thread.currentThread().interrupt();
+        }
+
+        log.info("表[{}] 全部完成! 总计处理: {}, 总计更新: {}", tableName, totalProcessed.get(), totalUpdated.get());
+    }
+
+
+    /**
+     * CASE WHEN 批量更新
+     */
+    private void batchCaseUpdate(String tableName, String idColumn, String phoneColumn, List<Map<String, Object>> list) {
+        StringBuilder sql = new StringBuilder("UPDATE " + tableName + " SET " + phoneColumn + " = CASE " + idColumn + " ");
+        for (int i = 0; i < list.size(); i++) {
+            sql.append("WHEN ? THEN ? ");
+        }
+        sql.append("END WHERE " + idColumn + " IN (");
+        for (int i = 0; i < list.size(); i++) {
+            sql.append(i > 0 ? ",?" : "?");
+        }
+        sql.append(")");
+
+        List<Object> params = new ArrayList<>();
+        for (Map<String, Object> row : list) {
+            params.add(row.get(idColumn));
+            params.add(row.get(phoneColumn));
+        }
+        for (Map<String, Object> row : list) {
+            params.add(row.get(idColumn));
+        }
+
+        jdbcTemplate.update(sql.toString(), params.toArray());
+    }
+
+
+//    public void Carrybusiness() {
+//        long lastId = 0L;
+//        int totalProcessed = 0;
+//        int totalNormal = 0;
+//        int totalUpdated = 0;
+//
+//        while (true) {
+//            List<FsUser> batch = fsUserMapper.getUserList(lastId, BATCH_SIZE);
+//            if (batch == null || batch.isEmpty()) {
+//                break;
+//            }
+//
+//            List<FsUser> toUpdateList = new ArrayList<>();
+//
+//            for (FsUser user : batch) {
+//                String phone = user.getPhone();
+//                if (phone != null && PHONE_PATTERN.matcher(phone.trim()).matches()) {
+//                    totalNormal++;
+//                    String encrypted = encryptPhone(phone.trim());
+//                    if (encrypted != null && !encrypted.isEmpty()) {
+//                        FsUser update = new FsUser();
+//                        update.setUserId(user.getUserId());
+//                        update.setPhone(encrypted);
+//                        toUpdateList.add(update);
+//                    }
+//                }
+//            }
+//
+//            if (!toUpdateList.isEmpty()) {
+//                for (int i = 0; i < toUpdateList.size(); i += UPDATE_BATCH_SIZE) {
+//                    int end = Math.min(i + UPDATE_BATCH_SIZE, toUpdateList.size());
+//                    List<FsUser> subList = toUpdateList.subList(i, end);
+//                    fsUserMapper.batchUpdatePhone(subList);
+//                    totalUpdated += subList.size();
+//                }
+//            }
+//
+//            totalProcessed += batch.size();
+//            lastId = batch.get(batch.size() - 1).getUserId();
+//
+//            log.info("已处理: {} 条, 正常手机号累计: {}, 已更新累计: {}, 当前游标: {}", totalProcessed, totalNormal, totalUpdated, lastId);
+//        }
+//
+//        log.info("处理完成! 总计: {} 条, 正常手机号: {} 个, 已更新: {} 个", totalProcessed, totalNormal, totalUpdated);
+//    }
+
+
+    @GetMapping("jiemishouji")
+    public String jiemishouji(String phone) {
+        return decryptPhone(phone);
+    }
+
+}

+ 15 - 6
fs-user-app/src/main/java/com/fs/app/controller/WxUserController.java

@@ -4,6 +4,7 @@ import cn.hutool.core.date.DateTime;
 import com.fs.common.core.domain.R;
 import com.fs.common.exception.CustomException;
 import com.fs.common.utils.OrderUtils;
+import com.fs.common.utils.PhoneUtils;
 import com.fs.store.param.LoginMpWxParam;
 import com.fs.common.utils.IpUtil;
 import com.fs.wx.miniapp.config.WxMaConfiguration;
@@ -64,11 +65,17 @@ public class WxUserController extends AppBaseController{
             this.logger.info(session.getOpenid());
             // 解密
             WxMaPhoneNumberInfo phoneNoInfo = wxService.getUserService().getPhoneNoInfo(session.getSessionKey(), param.getEncryptedData(), param.getIv());
+            // 对手机号进行加密,存入数据库
+            String plainPhone = phoneNoInfo.getPhoneNumber();
+            String encryptedPhone = PhoneUtils.encryptPhone(plainPhone);
             FsUser user=userService.selectFsUserByMaOpenId(session.getOpenid());
             String ip = IpUtil.getRequestIp();
             if(user==null){
-                //查询手机号是否存在,如果存在,更新
-                FsUser checkPhone=userService.selectFsUserByPhone(phoneNoInfo.getPhoneNumber());
+                //查询手机号是否存在(兼容加密数据和未加密数据两种情况)
+                FsUser checkPhone=userService.selectFsUserByPhone(encryptedPhone);
+                if(checkPhone==null){
+                    checkPhone=userService.selectFsUserByPhone(plainPhone);
+                }
                 if(checkPhone!=null){
                     if(checkPhone.getMaOpenId()==null){
                         user=checkPhone;
@@ -77,6 +84,8 @@ public class WxUserController extends AppBaseController{
                         userMap.setUserId(checkPhone.getUserId());
                         userMap.setUpdateTime(new DateTime());
                         userMap.setLastIp(ip);
+                        // 如果是明文手机号,同步更新为加密手机号
+                        userMap.setPhone(encryptedPhone);
                         userService.updateFsUser(userMap);
                     }
                     else{
@@ -95,8 +104,8 @@ public class WxUserController extends AppBaseController{
                         }
                     }
                     user.setUserCode(OrderUtils.genUserCode());
-                    user.setPhone(phoneNoInfo.getPhoneNumber());
-                    user.setUsername(phoneNoInfo.getPhoneNumber());
+                    user.setPhone(encryptedPhone);
+                    user.setUsername(plainPhone);
                     user.setNickname("微信用户");
                     user.setStatus(1);
                     user.setMaOpenId(session.getOpenid());
@@ -108,7 +117,7 @@ public class WxUserController extends AppBaseController{
            }
            else{
                FsUser userMap=new FsUser();
-               userMap.setPhone(phoneNoInfo.getPhoneNumber());
+               userMap.setPhone(encryptedPhone);
                userMap.setUserId(user.getUserId());
                userMap.setUpdateTime(new DateTime());
                userMap.setLastIp(ip);
@@ -190,4 +199,4 @@ public class WxUserController extends AppBaseController{
 
     }
 
-}
+}

+ 1 - 1
fs-user-app/src/main/resources/application-dev.yml

@@ -27,7 +27,7 @@ spring:
         druid:
             # 主库数据源
             master:
-                url: jdbc:mysql://172.16.0.31:3306/fs_sljx_hospital?allowMultiQueries=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                url: jdbc:mysql://gz-cdb-f32osvnt.sql.tencentcdb.com:25835/fs_sljx_hospital?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                 username: root
                 password: Ylrz_1q2w3e4r5t6y
             # 从库数据源

+ 252 - 0
fs-user-app/src/test/java/com/fs/DataMigrationTest.java

@@ -0,0 +1,252 @@
+package com.fs;
+
+import com.fs.common.utils.PhoneUtils;
+import com.fs.core.security.BaseSpringBootTest;
+import com.fs.store.domain.FsStoreOrder;
+import com.fs.store.domain.FsUser;
+import com.fs.store.domain.FsUserAddress;
+import com.fs.store.mapper.FsStoreOrderMapper;
+import com.fs.store.mapper.FsUserAddressMapper;
+import com.fs.store.mapper.FsUserMapper;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Encrypt phone/user_phone fields directly in fs_user, fs_user_address, fs_store_order
+ *
+ * @author fs
+ */
+public class DataMigrationTest extends BaseSpringBootTest {
+
+    @Autowired
+    private FsUserMapper fsUserMapper;
+
+    @Autowired
+    private FsUserAddressMapper fsUserAddressMapper;
+
+    @Autowired
+    private FsStoreOrderMapper fsStoreOrderMapper;
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    @Test
+    public void migrateData() {
+        logger.info("=== 开始加密手机号字段 ===");
+
+        // 1. encrypt fs_user.phone
+//        encryptUserPhone();
+
+        // 2. encrypt fs_user_address.phone
+        encryptUserAddressPhone();
+
+        // 3. encrypt fs_store_order.user_phone
+//        encryptStoreOrderPhone();
+
+        logger.info("=== 手机号加密完成! ===");
+    }
+
+    // ==================== fs_user ====================
+
+    private void encryptUserPhone() {
+        logger.info("encryptUserPhone start");
+        FsUser query = new FsUser();
+        List<FsUser> userList = fsUserMapper.selectFsUserList(query);
+        int total = userList.size();
+        logger.info("user count: {}", total);
+
+        int threadCount = 10;
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+        AtomicInteger successCount = new AtomicInteger(0);
+        AtomicInteger failCount = new AtomicInteger(0);
+        AtomicInteger skipCount = new AtomicInteger(0);
+
+        int batchSize = (total + threadCount - 1) / threadCount;
+        for (int i = 0; i < threadCount; i++) {
+            int fromIndex = i * batchSize;
+            int toIndex = Math.min(fromIndex + batchSize, total);
+            if (fromIndex >= total) {
+                break;
+            }
+            List<FsUser> batch = userList.subList(fromIndex, toIndex);
+            executor.submit(() -> {
+                for (FsUser user : batch) {
+                    try {
+                        String phone = user.getPhone();
+                        if (phone == null || phone.isEmpty()) {
+                            skipCount.incrementAndGet();
+                            continue;
+                        }
+                        String encryptedPhone = PhoneUtils.encryptPhone(phone);
+                        if (encryptedPhone != null && !encryptedPhone.isEmpty() && !encryptedPhone.equals(phone)) {
+                            updateUserPhone(user.getUserId(), encryptedPhone);
+                            int count = successCount.incrementAndGet();
+                            if (count % 100 == 0) {
+                                logger.info("encryptUserPhone progress: {}", count);
+                            }
+                        } else {
+                            skipCount.incrementAndGet();
+                        }
+                    } catch (Exception e) {
+                        logger.error("encryptUserPhone error, userId: {}", user.getUserId(), e);
+                        failCount.incrementAndGet();
+                    }
+                }
+            });
+        }
+
+        executor.shutdown();
+        try {
+            while (!executor.awaitTermination(60, java.util.concurrent.TimeUnit.SECONDS)) {
+                logger.info("waiting thread pool...");
+            }
+        } catch (InterruptedException e) {
+            logger.error("thread pool interrupted", e);
+            Thread.currentThread().interrupt();
+        }
+        logger.info("encryptUserPhone done, success: {}, skip: {}, fail: {}", successCount.get(), skipCount.get(), failCount.get());
+    }
+
+    private void updateUserPhone(Long userId, String encryptedPhone) {
+        String sql = "UPDATE fs_user SET phone = ? WHERE user_id = ?";
+        jdbcTemplate.update(sql, encryptedPhone, userId);
+    }
+
+    // ==================== fs_user_address ====================
+
+    private void encryptUserAddressPhone() {
+        logger.info("encryptUserAddressPhone start");
+        FsUserAddress query = new FsUserAddress();
+        List<FsUserAddress> addressList = fsUserAddressMapper.selectFsUserAddressList(query);
+        int total = addressList.size();
+        logger.info("address count: {}", total);
+
+        int threadCount = 10;
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+        AtomicInteger successCount = new AtomicInteger(0);
+        AtomicInteger failCount = new AtomicInteger(0);
+        AtomicInteger skipCount = new AtomicInteger(0);
+
+        int batchSize = (total + threadCount - 1) / threadCount;
+        for (int i = 0; i < threadCount; i++) {
+            int fromIndex = i * batchSize;
+            int toIndex = Math.min(fromIndex + batchSize, total);
+            if (fromIndex >= total) {
+                break;
+            }
+            List<FsUserAddress> batch = addressList.subList(fromIndex, toIndex);
+            executor.submit(() -> {
+                for (FsUserAddress address : batch) {
+                    try {
+                        String phone = address.getPhone();
+                        if (phone == null || phone.isEmpty()) {
+                            skipCount.incrementAndGet();
+                            continue;
+                        }
+                        String encryptedPhone = PhoneUtils.encryptPhone(phone);
+                        if (encryptedPhone != null && !encryptedPhone.isEmpty() && !encryptedPhone.equals(phone)) {
+                            updateUserAddressPhone(address.getId(), encryptedPhone);
+                            int count = successCount.incrementAndGet();
+                            if (count % 100 == 0) {
+                                logger.info("encryptUserAddressPhone progress: {}", count);
+                            }
+                        } else {
+                            skipCount.incrementAndGet();
+                        }
+                    } catch (Exception e) {
+                        logger.error("encryptUserAddressPhone error, id: {}", address.getId(), e);
+                        failCount.incrementAndGet();
+                    }
+                }
+            });
+        }
+
+        executor.shutdown();
+        try {
+            while (!executor.awaitTermination(60, java.util.concurrent.TimeUnit.SECONDS)) {
+                logger.info("waiting thread pool...");
+            }
+        } catch (InterruptedException e) {
+            logger.error("thread pool interrupted", e);
+            Thread.currentThread().interrupt();
+        }
+        logger.info("encryptUserAddressPhone done, success: {}, skip: {}, fail: {}", successCount.get(), skipCount.get(), failCount.get());
+    }
+
+    private void updateUserAddressPhone(Long id, String encryptedPhone) {
+        String sql = "UPDATE fs_user_address SET phone = ? WHERE id = ?";
+        jdbcTemplate.update(sql, encryptedPhone, id);
+    }
+
+    // ==================== fs_store_order ====================
+
+    private void encryptStoreOrderPhone() {
+        logger.info("encryptStoreOrderPhone start");
+        FsStoreOrder query = new FsStoreOrder();
+        List<FsStoreOrder> orderList = fsStoreOrderMapper.selectFsStoreOrderList(query);
+        int total = orderList.size();
+        logger.info("order count: {}", total);
+
+        int threadCount = 10;
+        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
+        AtomicInteger successCount = new AtomicInteger(0);
+        AtomicInteger failCount = new AtomicInteger(0);
+        AtomicInteger skipCount = new AtomicInteger(0);
+
+        int batchSize = (total + threadCount - 1) / threadCount;
+        for (int i = 0; i < threadCount; i++) {
+            int fromIndex = i * batchSize;
+            int toIndex = Math.min(fromIndex + batchSize, total);
+            if (fromIndex >= total) {
+                break;
+            }
+            List<FsStoreOrder> batch = orderList.subList(fromIndex, toIndex);
+            executor.submit(() -> {
+                for (FsStoreOrder order : batch) {
+                    try {
+                        String userPhone = order.getUserPhone();
+                        if (userPhone == null || userPhone.isEmpty()) {
+                            skipCount.incrementAndGet();
+                            continue;
+                        }
+                        String encryptedPhone = PhoneUtils.encryptPhone(userPhone);
+                        if (encryptedPhone != null && !encryptedPhone.isEmpty() && !encryptedPhone.equals(userPhone)) {
+                            updateStoreOrderPhone(order.getId(), encryptedPhone);
+                            int count = successCount.incrementAndGet();
+                            if (count % 100 == 0) {
+                                logger.info("encryptStoreOrderPhone progress: {}", count);
+                            }
+                        } else {
+                            skipCount.incrementAndGet();
+                        }
+                    } catch (Exception e) {
+                        logger.error("encryptStoreOrderPhone error, id: {}", order.getId(), e);
+                        failCount.incrementAndGet();
+                    }
+                }
+            });
+        }
+
+        executor.shutdown();
+        try {
+            while (!executor.awaitTermination(60, java.util.concurrent.TimeUnit.SECONDS)) {
+                logger.info("waiting thread pool...");
+            }
+        } catch (InterruptedException e) {
+            logger.error("thread pool interrupted", e);
+            Thread.currentThread().interrupt();
+        }
+        logger.info("encryptStoreOrderPhone done, success: {}, skip: {}, fail: {}", successCount.get(), skipCount.get(), failCount.get());
+    }
+
+    private void updateStoreOrderPhone(Long id, String encryptedPhone) {
+        String sql = "UPDATE fs_store_order SET user_phone = ? WHERE id = ?";
+        jdbcTemplate.update(sql, encryptedPhone, id);
+    }
+}