Files
cunkebao_v3/Moncter/app/service/UserService.php
2026-01-05 10:16:20 +08:00

396 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\service;
use app\repository\UserProfileRepository;
use app\utils\EncryptionHelper;
use app\utils\IdCardHelper;
use app\utils\LoggerHelper;
use Ramsey\Uuid\Uuid as UuidGenerator;
/**
* 用户服务
*
* 职责:
* - 创建用户(包含身份证加密)
* - 查询用户信息(支持解密身份证)
* - 根据身份证哈希匹配用户
*/
class UserService
{
public function __construct(
protected UserProfileRepository $userProfileRepository
) {
}
/**
* 创建用户
*
* @param array<string, mixed> $data 用户数据
* @return array<string, mixed> 创建的用户信息
* @throws \InvalidArgumentException
*/
public function createUser(array $data): array
{
// 验证必填字段
if (empty($data['id_card'])) {
throw new \InvalidArgumentException('身份证号不能为空');
}
$idCard = trim($data['id_card']);
$idCardType = $data['id_card_type'] ?? '身份证';
// 验证身份证格式(简单验证)
if ($idCardType === '身份证' && !$this->validateIdCard($idCard)) {
throw new \InvalidArgumentException('身份证号格式不正确');
}
// 检查是否已存在(通过身份证哈希)
$idCardHash = EncryptionHelper::hash($idCard);
$existingUser = $this->userProfileRepository->newQuery()
->where('id_card_hash', $idCardHash)
->first();
if ($existingUser) {
throw new \InvalidArgumentException('该身份证号已存在user_id: ' . $existingUser->user_id);
}
// 加密身份证
$idCardEncrypted = EncryptionHelper::encrypt($idCard);
// 生成用户ID
$userId = $data['user_id'] ?? UuidGenerator::uuid4()->toString();
$now = new \DateTimeImmutable('now');
// 从身份证号中自动提取基础信息(如果未提供)
$idCardInfo = IdCardHelper::extractInfo($idCard);
$gender = isset($data['gender']) ? (int)$data['gender'] : ($idCardInfo['gender'] > 0 ? $idCardInfo['gender'] : null);
$birthday = isset($data['birthday']) ? new \DateTimeImmutable($data['birthday']) : $idCardInfo['birthday'];
// 创建用户记录
$user = new UserProfileRepository();
$user->user_id = $userId;
$user->id_card_hash = $idCardHash;
$user->id_card_encrypted = $idCardEncrypted;
$user->id_card_type = $idCardType;
$user->name = $data['name'] ?? null;
$user->phone = $data['phone'] ?? null;
$user->address = $data['address'] ?? null;
$user->email = $data['email'] ?? null;
$user->gender = $gender;
$user->birthday = $birthday;
$user->total_amount = isset($data['total_amount']) ? (float)$data['total_amount'] : 0;
$user->total_count = isset($data['total_count']) ? (int)$data['total_count'] : 0;
$user->last_consume_time = isset($data['last_consume_time']) ? new \DateTimeImmutable($data['last_consume_time']) : null;
$user->status = isset($data['status']) ? (int)$data['status'] : 0;
$user->create_time = $now;
$user->update_time = $now;
$user->save();
LoggerHelper::logBusiness('user_created', [
'user_id' => $userId,
'name' => $user->name,
'id_card_type' => $idCardType,
]);
return [
'user_id' => $userId,
'name' => $user->name,
'phone' => $user->phone,
'id_card_type' => $idCardType,
'create_time' => $user->create_time,
];
}
/**
* 根据 user_id 获取用户信息
*
* @param string $userId 用户ID
* @param bool $decryptIdCard 是否解密身份证(需要权限控制)
* @return array<string, mixed>|null 用户信息
*/
public function getUserById(string $userId, bool $decryptIdCard = false): ?array
{
$user = $this->userProfileRepository->findByUserId($userId);
if (!$user) {
return null;
}
$result = [
'user_id' => $user->user_id,
'name' => $user->name,
'phone' => $user->phone,
'address' => $user->address,
'email' => $user->email,
'gender' => $user->gender,
'birthday' => $user->birthday,
'id_card_type' => $user->id_card_type,
'total_amount' => $user->total_amount,
'total_count' => $user->total_count,
'last_consume_time' => $user->last_consume_time,
'tags_update_time' => $user->tags_update_time,
'status' => $user->status,
'create_time' => $user->create_time,
'update_time' => $user->update_time,
];
// 如果需要解密身份证(需要权限控制)
if ($decryptIdCard) {
try {
$result['id_card'] = EncryptionHelper::decrypt($user->id_card_encrypted);
} catch (\Throwable $e) {
LoggerHelper::logError($e, ['user_id' => $userId, 'action' => 'decrypt_id_card']);
$result['id_card'] = null;
$result['decrypt_error'] = '解密失败';
}
} else {
// 返回脱敏的身份证
$result['id_card_encrypted'] = $user->id_card_encrypted;
}
return $result;
}
/**
* 根据身份证号查找用户(通过哈希匹配)
*
* @param string $idCard 身份证号
* @return array<string, mixed>|null 用户信息
*/
public function findUserByIdCard(string $idCard): ?array
{
$idCardHash = EncryptionHelper::hash($idCard);
$user = $this->userProfileRepository->newQuery()
->where('id_card_hash', $idCardHash)
->first();
if (!$user) {
return null;
}
return $this->getUserById($user->user_id, false);
}
/**
* 更新用户信息
*
* @param string $userId 用户ID
* @param array<string, mixed> $data 要更新的用户数据
* @return array<string, mixed> 更新后的用户信息
* @throws \InvalidArgumentException
*/
public function updateUser(string $userId, array $data): array
{
$user = $this->userProfileRepository->findByUserId($userId);
if (!$user) {
throw new \InvalidArgumentException("用户不存在: {$userId}");
}
$now = new \DateTimeImmutable('now');
// 更新允许修改的字段
if (isset($data['name'])) {
$user->name = $data['name'];
}
if (isset($data['phone'])) {
$user->phone = $data['phone'];
}
if (isset($data['email'])) {
$user->email = $data['email'];
}
if (isset($data['address'])) {
$user->address = $data['address'];
}
if (isset($data['gender'])) {
$user->gender = (int)$data['gender'];
}
if (isset($data['birthday'])) {
$user->birthday = new \DateTimeImmutable($data['birthday']);
}
if (isset($data['status'])) {
$user->status = (int)$data['status'];
}
$user->update_time = $now;
$user->save();
LoggerHelper::logBusiness('user_updated', [
'user_id' => $userId,
'updated_fields' => array_keys($data),
]);
return $this->getUserById($userId, false);
}
/**
* 删除用户(软删除,设置状态为禁用)
*
* @param string $userId 用户ID
* @return bool 是否删除成功
* @throws \InvalidArgumentException
*/
public function deleteUser(string $userId): bool
{
$user = $this->userProfileRepository->findByUserId($userId);
if (!$user) {
throw new \InvalidArgumentException("用户不存在: {$userId}");
}
// 软删除:设置状态为禁用
$user->status = 1; // 1 表示禁用
$user->update_time = new \DateTimeImmutable('now');
$user->save();
LoggerHelper::logBusiness('user_deleted', [
'user_id' => $userId,
]);
return true;
}
/**
* 搜索用户(支持多种条件组合)
*
* @param array<string, mixed> $conditions 搜索条件
* - name: 姓名(模糊搜索)
* - phone: 手机号(精确或模糊)
* - email: 邮箱(精确或模糊)
* - id_card: 身份证号(精确匹配)
* - gender: 性别0-未知1-男2-女)
* - status: 状态0-正常1-禁用)
* - min_total_amount: 最小总消费金额
* - max_total_amount: 最大总消费金额
* - min_total_count: 最小消费次数
* - max_total_count: 最大消费次数
* @param int $page 页码从1开始
* @param int $pageSize 每页数量
* @return array<string, mixed> 返回用户列表和分页信息
*/
public function searchUsers(array $conditions, int $page = 1, int $pageSize = 20): array
{
$query = $this->userProfileRepository->newQuery();
// 姓名模糊搜索MongoDB 使用正则表达式)
if (!empty($conditions['name'])) {
$namePattern = preg_quote($conditions['name'], '/');
$query->where('name', 'regex', "/{$namePattern}/i");
}
// 手机号搜索(支持精确和模糊)
if (!empty($conditions['phone'])) {
if (isset($conditions['phone_exact']) && $conditions['phone_exact']) {
// 精确匹配
$query->where('phone', $conditions['phone']);
} else {
// 模糊匹配MongoDB 使用正则表达式)
$phonePattern = preg_quote($conditions['phone'], '/');
$query->where('phone', 'regex', "/{$phonePattern}/i");
}
}
// 邮箱搜索(支持精确和模糊)
if (!empty($conditions['email'])) {
if (isset($conditions['email_exact']) && $conditions['email_exact']) {
// 精确匹配
$query->where('email', $conditions['email']);
} else {
// 模糊匹配MongoDB 使用正则表达式)
$emailPattern = preg_quote($conditions['email'], '/');
$query->where('email', 'regex', "/{$emailPattern}/i");
}
}
// 如果指定了 user_ids限制搜索范围
if (!empty($conditions['user_ids']) && is_array($conditions['user_ids'])) {
$query->whereIn('user_id', $conditions['user_ids']);
}
// 身份证号精确匹配(通过哈希)
if (!empty($conditions['id_card'])) {
$idCardHash = EncryptionHelper::hash($conditions['id_card']);
$query->where('id_card_hash', $idCardHash);
}
// 性别筛选
if (isset($conditions['gender']) && $conditions['gender'] !== '') {
$query->where('gender', (int)$conditions['gender']);
}
// 状态筛选
if (isset($conditions['status']) && $conditions['status'] !== '') {
$query->where('status', (int)$conditions['status']);
}
// 总消费金额范围
if (isset($conditions['min_total_amount'])) {
$query->where('total_amount', '>=', (float)$conditions['min_total_amount']);
}
if (isset($conditions['max_total_amount'])) {
$query->where('total_amount', '<=', (float)$conditions['max_total_amount']);
}
// 消费次数范围
if (isset($conditions['min_total_count'])) {
$query->where('total_count', '>=', (int)$conditions['min_total_count']);
}
if (isset($conditions['max_total_count'])) {
$query->where('total_count', '<=', (int)$conditions['max_total_count']);
}
// 分页
$total = $query->count();
$users = $query->skip(($page - 1) * $pageSize)
->take($pageSize)
->orderBy('create_time', 'desc')
->get();
// 转换为数组格式
$result = [];
foreach ($users as $user) {
$result[] = [
'user_id' => $user->user_id,
'name' => $user->name,
'phone' => $user->phone,
'email' => $user->email,
'address' => $user->address,
'gender' => $user->gender,
'birthday' => $user->birthday,
'id_card_type' => $user->id_card_type,
'total_amount' => $user->total_amount,
'total_count' => $user->total_count,
'last_consume_time' => $user->last_consume_time,
'tags_update_time' => $user->tags_update_time,
'status' => $user->status,
'create_time' => $user->create_time,
'update_time' => $user->update_time,
];
}
return [
'users' => $result,
'total' => $total,
'page' => $page,
'page_size' => $pageSize,
'total_pages' => (int)ceil($total / $pageSize),
];
}
/**
* 验证身份证号格式(简单验证)
*
* @param string $idCard 身份证号
* @return bool
*/
protected function validateIdCard(string $idCard): bool
{
// 15位或18位数字最后一位可能是X
return preg_match('/^(\d{15}|\d{17}[\dXx])$/', $idCard) === 1;
}
}