Files
MrleeAdmin/backend/app/service/api/member/MemberService.php
T
2026-08-31 11:33:59 +08:00

363 lines
9.8 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
declare(strict_types=1);
namespace app\service\api\member;
use app\api\CurrentMember;
use app\dao\member\MemberDao;
use app\dao\member\MemberThirdPartyDao;
use app\model\member\Member;
use app\model\member\MemberAddress;
use app\enum\common\EnabledStatus;
use core\base\BaseService;
use support\Container;
use support\Redis;
/**
* 会员用户服务
*/
class MemberService extends BaseService
{
/**
* 构造方法
*/
public function __construct(MemberDao $dao)
{
$this->dao = $dao;
}
/**
* 更新会员信息
*
* @throws \Exception
* @throws \Throwable
*/
public function updateInfo(array $data): void
{
$currentMember = Container::make(CurrentMember::class);
$id = $currentMember->id();
if (empty($id)) {
throw new \Exception('用户未登录', 401);
}
$model = $this->dao->get($id);
if (!$model) {
throw new \Exception('用户不存在', 401);
}
$allowedFields = ['nickname', 'avatar', 'gender', 'birthday','bio'];
$updateData = [];
foreach ($allowedFields as $field) {
if (isset($data[$field])) {
$updateData[$field] = $data[$field];
}
}
if (empty($updateData)) {
throw new \Exception('没有可更新的数据', 400);
}
$model->fill($updateData);
if (!$model->isDirty()) {
throw new \Exception('数据没有变化', 400);
}
$model->save();
}
/**
* 绑定会员手机号
*/
public function bindPhone(array $data): array
{
if (empty($data['phone']) || empty($data['verify_code'])) {
throw new \Exception('手机号和验证码不能为空', 400);
}
// 验证短信验证码
$this->verifySmsCode($data['phone'], $data['verify_code']);
$memberId = $this->getCurrentMemberId();
$member = Member::find($memberId);
if (!$member) {
throw new \Exception('用户不存在', 401);
}
// 检查手机号是否已被其他用户绑定
$existingMember = Member::where('phone', $data['phone'])
->where('id', '!=', $memberId)
->first();
if ($existingMember) {
throw new \Exception('手机号已被其他用户绑定', 400);
}
// 更新手机号
$member->phone = $data['phone'];
$member->save();
return [
'code' => 200,
'msg' => '绑定成功',
];
}
/**
* 修改密码
*
* @throws \Exception
*/
public function changePassword(array $data): void
{
if (empty($data['old_password']) || empty($data['new_password'])) {
throw new \Exception('旧密码和新密码不能为空', 400);
}
$currentMember = Container::make(CurrentMember::class);
$id = $currentMember->id();
if (empty($id)) {
throw new \Exception('用户未登录', 401);
}
$model = $this->dao->get($id);
if (!$model) {
throw new \Exception('用户不存在', 401);
}
if (!$model->verifyPassword($data['old_password'])) {
throw new \Exception('旧密码错误', 400);
}
$model->password = $data['new_password'];
$model->save();
}
/**
* 获取地址列表
*/
public function getAddressList(): array
{
$memberId = $this->getCurrentMemberId();
$addresses = MemberAddress::where('member_id', $memberId)
->where('enabled', EnabledStatus::ENABLED->value)
->orderBy('is_default', 'desc')
->orderBy('id', 'desc')
->get();
return $addresses->toArray();
}
/**
* 创建地址
*/
public function createAddress(array $data): array
{
if (empty($data['name']) || empty($data['phone']) || empty($data['province']) ||
empty($data['city']) || empty($data['district']) || empty($data['address'])) {
throw new \Exception('请填写完整的地址信息', 400);
}
$memberId = $this->getCurrentMemberId();
// 如果设置为默认地址,取消其他默认地址
if ($data['is_default'] ?? false) {
MemberAddress::where('member_id', $memberId)
->where('is_default', true)
->update(['is_default' => false]);
}
$addressData = [
'member_id' => $memberId,
'name' => $data['name'],
'phone' => $data['phone'],
'province' => $data['province'],
'city' => $data['city'],
'district' => $data['district'],
'address' => $data['address'],
'is_default' => $data['is_default'] ?? false,
'enabled' => EnabledStatus::ENABLED->value,
];
$address = MemberAddress::create($addressData);
return ['id' => $address->id];
}
/**
* 更新地址
*/
public function updateAddress(int $id, array $data): array
{
$memberId = $this->getCurrentMemberId();
$address = MemberAddress::where('id', $id)
->where('member_id', $memberId)
->first();
if (!$address) {
throw new \Exception('地址不存在', 404);
}
// 如果设置为默认地址,取消其他默认地址
if ($data['is_default'] ?? false) {
MemberAddress::where('member_id', $memberId)
->where('id', '!=', $id)
->where('is_default', true)
->update(['is_default' => false]);
}
// 允许更新的字段
$allowedFields = ['name', 'phone', 'province', 'city', 'district', 'address', 'is_default'];
$updateData = [];
foreach ($allowedFields as $field) {
if (isset($data[$field])) {
$updateData[$field] = $data[$field];
}
}
if (empty($updateData)) {
throw new \Exception('没有可更新的数据', 400);
}
$address->update($updateData);
return [];
}
/**
* 删除地址
*/
public function deleteAddress(int $id): array
{
$memberId = $this->getCurrentMemberId();
$address = MemberAddress::where('id', $id)
->where('member_id', $memberId)
->first();
if (!$address) {
throw new \Exception('地址不存在', 404);
}
// 软删除
$address->enabled = EnabledStatus::DISABLED->value;
$address->save();
return [];
}
/**
* 验证短信验证码
*/
private function verifySmsCode(string $mobile, string $code): void
{
$cacheKey = 'sms_code:' . $mobile;
$cachedCode = Redis::get($cacheKey);
if (!$cachedCode || $cachedCode !== $code) {
throw new \Exception('验证码错误或已过期', 400);
}
// 验证成功后删除验证码
Redis::del($cacheKey);
}
/**
* 获取当前用户ID
*/
private function getCurrentMemberId(): int
{
$request = \request();
$token = $request->header('Authorization');
if (!$token) {
throw new \Exception('未登录', 401);
}
$tokenData = Redis::get('user_token:' . md5($token));
if (!$tokenData) {
throw new \Exception('登录已过期', 401);
}
$data = json_decode($tokenData, true);
return $data['member_id'] ?? 0;
}
/**
* 上传头像
*/
public function uploadAvatar(\Webman\Http\UploadFile $file): array
{
$memberId = $this->getCurrentMemberId();
// 验证文件类型
$allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp'];
if (!in_array($file->getUploadMimeType(), $allowedTypes)) {
throw new \Exception('只支持jpg、png、gif、webp格式的图片', 400);
}
// 验证文件大小(最大2MB)
$maxSize = 2 * 1024 * 1024;
if ($file->getSize() > $maxSize) {
throw new \Exception('图片大小不能超过2MB', 400);
}
// 生成文件名
$extension = pathinfo($file->getUploadName(), PATHINFO_EXTENSION);
$filename = 'avatar_' . $memberId . '_' . time() . '.' . $extension;
// 保存文件
$uploadPath = public_path() . '/uploads/avatar/';
if (!is_dir($uploadPath)) {
mkdir($uploadPath, 0755, true);
}
$file->moveTo($uploadPath . $filename);
// 生成访问URL
$url = '/uploads/avatar/' . $filename;
// 更新会员头像
$member = Member::find($memberId);
if ($member) {
$member->avatar = $url;
$member->save();
}
return [
'url' => $url,
'filename' => $filename
];
}
/**
* 获取活跃用户
*/
public function getActiveUsers(int $limit = 10): array
{
$member = $this->dao->getActiveUsers($limit);
return $member->map(function ($item) {
return [
'id' => $item->id,
'username' => $item->username,
'nickname' => $item->nickname,
'avatar' => $item->avatar,
'intro' => $item->intro ?? "",
'last_active' => $item->last_time,
];
})->toArray();
}
/**
* 检查用户是否是管理员
* 暂时返回 false,只有问题作者可以设置最佳答案
*/
public function isAdmin(int|string $userId): bool
{
return false;
}
}