初始化本地仓库
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\service\admin\member;
|
||||
|
||||
use app\dao\web\MenuDao;
|
||||
use app\dao\member\MemberDao;
|
||||
use app\service\admin\web\MenuService as SiteMenuService;
|
||||
use core\base\BaseService;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use support\Container;
|
||||
|
||||
class MemberAuthService extends BaseService
|
||||
{
|
||||
|
||||
public function __construct(MemberDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单
|
||||
*
|
||||
* @param string|int $memberId 会员ID
|
||||
* @param bool $includeEmptyCode 是否包含权限码为空的菜单(不需要标签权限的公共菜单)
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Collection|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getMenusByMemberTags(string|int $memberId, bool $includeEmptyCode = false): ?Collection
|
||||
{
|
||||
if (empty($memberId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$member = $this->dao->find($memberId);
|
||||
if (!$member) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$menuIds = [];
|
||||
$tags = $member->tags()->where('enabled', 1)->get();
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
$permissions = $tag->permissions()->get();
|
||||
foreach ($permissions as $permission) {
|
||||
$menuIds[] = $permission->id;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($menuIds) && !$includeEmptyCode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$menuIds = array_unique($menuIds);
|
||||
$menuService = new SiteMenuService(new MenuDao());
|
||||
$menus = $this->getMenusByIds($menuService, $menuIds, $includeEmptyCode);
|
||||
|
||||
return $menus->where('menu_type', '!=', 'button');
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标签ID获取菜单
|
||||
*
|
||||
* @param string|int $tagId 标签ID
|
||||
* @param bool $includeEmptyCode 是否包含权限码为空的菜单(不需要标签权限的公共菜单)
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Collection|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getMenusByTagId(string|int $tagId, bool $includeEmptyCode = false): ?Collection
|
||||
{
|
||||
if (empty($tagId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$menuIds = [];
|
||||
/** @var MemberTagService $memberTagService */
|
||||
$memberTagService = Container::make(MemberTagService::class);
|
||||
$memberTag = $memberTagService->get($tagId);
|
||||
if (!$memberTag) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$permissions = $memberTag->permissions()->get();
|
||||
foreach ($permissions as $permission) {
|
||||
$menuIds[] = $permission->id;
|
||||
}
|
||||
|
||||
if (empty($menuIds) && !$includeEmptyCode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$menuIds = array_unique($menuIds);
|
||||
$menuService = new SiteMenuService(new MenuDao());
|
||||
return $this->getMenusByIds($menuService, $menuIds, $includeEmptyCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有菜单
|
||||
*
|
||||
* @param bool $includeEmptyCode 是否包含权限码为空的菜单(不需要标签权限的公共菜单)
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getAllMenus(bool $includeEmptyCode = false): Collection
|
||||
{
|
||||
/** @var $siteMenuService SiteMenuService */
|
||||
$siteMenuService = Container::make(SiteMenuService::class);
|
||||
$menuModel = $siteMenuService->dao->getModel();
|
||||
|
||||
if ($includeEmptyCode) {
|
||||
return $menuModel->where('enabled', 1)
|
||||
->orderBy('sort')
|
||||
->get();
|
||||
}
|
||||
|
||||
return $menuModel->where('enabled', 1)
|
||||
->where(function ($q) {
|
||||
$q->whereNotNull('code')->where('code', '!=', '');
|
||||
})
|
||||
->orderBy('sort')
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据IDS输出菜单
|
||||
*
|
||||
* @param \app\service\admin\web\MenuService $menuService
|
||||
* @param array $ids
|
||||
* @param bool $includeEmptyCode 是否包含权限码为空的菜单
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function getMenusByIds(SiteMenuService $menuService, array $ids, bool $includeEmptyCode = true): Collection
|
||||
{
|
||||
$menuModel = $menuService->dao->getModel();
|
||||
|
||||
// 如果需要包含权限码为空的菜单
|
||||
if ($includeEmptyCode) {
|
||||
$query = $menuModel->where('enabled', 1);
|
||||
|
||||
// 如果有指定菜单ID,则包含这些菜单和权限码为空的菜单
|
||||
if (!empty($ids)) {
|
||||
$query->where(function ($q) use ($ids) {
|
||||
$q->whereIn('id', $ids)->orWhereNull('code')->orWhere('code', '');
|
||||
});
|
||||
} else {
|
||||
// 没有指定菜单ID时,只返回权限码为空的菜单
|
||||
$query->where(function ($q) {
|
||||
$q->whereNull('code')->orWhere('code', '');
|
||||
});
|
||||
}
|
||||
|
||||
return $query->orderBy('sort')->get();
|
||||
}
|
||||
|
||||
// 不包含权限码为空的菜单,只返回指定ID的菜单
|
||||
if (empty($ids)) {
|
||||
return new Collection();
|
||||
}
|
||||
|
||||
$chunkSize = 200;
|
||||
$chunks = array_chunk($ids, $chunkSize);
|
||||
|
||||
// 分块查询 + 批量合并(减少内存占用)
|
||||
$allResults = new Collection();
|
||||
foreach ($chunks as $chunk) {
|
||||
$results = $menuModel
|
||||
->whereIn('id', $chunk)
|
||||
->where('enabled', 1)
|
||||
->orderBy('sort')
|
||||
->get();
|
||||
$allResults = $allResults->merge($results);
|
||||
}
|
||||
return $allResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户角色-权限码codes
|
||||
*
|
||||
* @param $memberId
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getCodesByUserTags($memberId): array
|
||||
{
|
||||
if (empty($memberId)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$member = $this->dao->find($memberId);
|
||||
if (!$member) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$codes = [];
|
||||
$tags = $member->tags()->where('enabled', 1)->get();
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
$permissions = $tag->permissions()->get();
|
||||
foreach ($permissions as $permission) {
|
||||
if (!empty($permission->code)) {
|
||||
$codes[] = $permission->code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($codes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\service\admin\member;
|
||||
|
||||
use app\dao\member\MemberBillDao;
|
||||
use app\dao\member\MemberDao;
|
||||
use core\base\BaseService;
|
||||
use core\exception\handler\AdminException;
|
||||
|
||||
/**
|
||||
* 会员账单服务类
|
||||
*/
|
||||
class MemberBillService extends BaseService
|
||||
{
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
*/
|
||||
public function __construct(MemberBillDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 余额操作
|
||||
*/
|
||||
public function operate(array $data): array
|
||||
{
|
||||
$member = $this->memberDao->get($data['member_id']);
|
||||
if (!$member) {
|
||||
throw new AdminException('会员不存在');
|
||||
}
|
||||
|
||||
$amount = (float)$data['amount'];
|
||||
$type = (int)$data['type'];
|
||||
$remark = $data['remark'] ?? '';
|
||||
|
||||
// 开始事务
|
||||
$this->dao->getModel()->getConnection()->beginTransaction();
|
||||
try {
|
||||
// 更新会员余额
|
||||
if ($type == 1) {
|
||||
$member->balance += $amount;
|
||||
} else {
|
||||
if ($member->balance < $amount) {
|
||||
throw new AdminException('余额不足');
|
||||
}
|
||||
$member->balance -= $amount;
|
||||
}
|
||||
$member->save();
|
||||
|
||||
// 记录账单
|
||||
$billData = [
|
||||
'member_id' => $data['member_id'],
|
||||
'amount' => $amount,
|
||||
'type' => $type,
|
||||
'balance' => $member->balance,
|
||||
'remark' => $remark,
|
||||
'create_time' => time(),
|
||||
];
|
||||
$bill = $this->dao->save($billData);
|
||||
|
||||
$this->dao->getModel()->getConnection()->commit();
|
||||
return $bill->toArray();
|
||||
} catch (\Exception $e) {
|
||||
throw new AdminException($e->getMessage(), ['errorCode' => $e->getCode() ?: -1]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量余额操作
|
||||
*/
|
||||
public function batchOperate(array $data): array
|
||||
{
|
||||
$memberIds = $data['member_ids'] ?? [];
|
||||
if (empty($memberIds)) {
|
||||
throw new AdminException('请选择会员');
|
||||
}
|
||||
|
||||
$results = [];
|
||||
foreach ($memberIds as $memberId) {
|
||||
try {
|
||||
$operateData = array_merge($data, ['member_id' => $memberId]);
|
||||
$result = $this->operate($operateData);
|
||||
$results[] = $result;
|
||||
} catch (xception $e) {
|
||||
$results[] = ['member_id' => $memberId, 'error' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取余额统计
|
||||
*/
|
||||
public function getStatistics(array $params): array
|
||||
{
|
||||
$startTime = $params['start_time'] ?? null;
|
||||
$endTime = $params['end_time'] ?? null;
|
||||
|
||||
$where = [];
|
||||
if ($startTime) {
|
||||
$where[] = ['create_time', '>=', strtotime($startTime)];
|
||||
}
|
||||
if ($endTime) {
|
||||
$where[] = ['create_time', '<=', strtotime($endTime) + 86399];
|
||||
}
|
||||
|
||||
$totalIncome = $this->dao->query()->where($where)->where('type', 1)->sum('amount') ?: 0;
|
||||
$totalExpense = $this->dao->query()->where($where)->where('type', 2)->sum('amount') ?: 0;
|
||||
$totalRecords = $this->dao->query()->where($where)->count();
|
||||
|
||||
return [
|
||||
'total_income' => $totalIncome,
|
||||
'total_expense' => $totalExpense,
|
||||
'net_income' => $totalIncome - $totalExpense,
|
||||
'total_records' => $totalRecords,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\service\admin\member;
|
||||
|
||||
use app\dao\member\MemberDao;
|
||||
use app\dao\member\MemberLevelDao;
|
||||
use core\base\BaseService;
|
||||
use core\exception\handler\AdminException;
|
||||
|
||||
/**
|
||||
* 会员等级服务类
|
||||
*/
|
||||
class MemberLevelService extends BaseService
|
||||
{
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
*/
|
||||
public function __construct(MemberLevelDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\service\admin\member;
|
||||
|
||||
use app\dao\member\MemberDao;
|
||||
use app\dao\member\MemberPointsDao;
|
||||
use app\enum\member\PointType;
|
||||
use app\enum\member\PointSource;
|
||||
use app\adminapi\event\PointsChangedEvent;
|
||||
use core\base\BaseService;
|
||||
use core\exception\handler\AdminException;
|
||||
use support\Container;
|
||||
|
||||
/**
|
||||
* 会员积分服务类
|
||||
*/
|
||||
class MemberPointsService extends BaseService
|
||||
{
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
*/
|
||||
public function __construct(MemberPointsDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 积分操作
|
||||
*
|
||||
* @throws \core\exception\handler\AdminException
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function operate(array $data): void
|
||||
{
|
||||
try {
|
||||
$this->transaction(function () use ($data) {
|
||||
$memberDao = Container::make(MemberDao::class);
|
||||
$member = $memberDao->get($data['member_id']);
|
||||
$oldPoints = $member->points;
|
||||
if (!$member) {
|
||||
throw new AdminException('会员不存在');
|
||||
}
|
||||
$points = (int)$data['points'];
|
||||
$type = (int)$data['type'];
|
||||
$remark = $data['remark'] ?? '';
|
||||
|
||||
// 更新会员积分
|
||||
if ($type == PointType::INCREASE->value) {
|
||||
$member->points += $points;
|
||||
}
|
||||
if ($type == PointType::DECREASE->value) {
|
||||
if ($member->points < $points) {
|
||||
throw new AdminException('积分不足');
|
||||
}
|
||||
$member->points -= $points;
|
||||
}
|
||||
if($type == PointType::ADJUST->value){
|
||||
$member->points = $points;
|
||||
}
|
||||
$member->save();
|
||||
// 记录积分变动
|
||||
$pointsData = [
|
||||
'member_id' => $member->id,
|
||||
'points' => $points,
|
||||
'type' => $type,
|
||||
'balance' => $member->points,
|
||||
'remark' => $remark,
|
||||
];
|
||||
$this->dao->save($pointsData);
|
||||
|
||||
$event = new PointsChangedEvent(
|
||||
$member->id,
|
||||
$points,
|
||||
PointSource::ADMIN,
|
||||
PointType::INCREASE,
|
||||
$oldPoints,
|
||||
$member->points,
|
||||
$remark
|
||||
);
|
||||
$event->dispatch();
|
||||
});
|
||||
|
||||
} catch (\Exception $e) {
|
||||
throw new AdminException($e->getMessage());
|
||||
}
|
||||
}
|
||||
//
|
||||
// /**
|
||||
// * 批量积分操作
|
||||
// */
|
||||
// public function batchOperate(array $data): array
|
||||
// {
|
||||
// $memberIds = $data['member_ids'] ?? [];
|
||||
// if (empty($memberIds)) {
|
||||
// throw new AdminException('请选择会员');
|
||||
// }
|
||||
//
|
||||
// $results = [];
|
||||
// foreach ($memberIds as $memberId) {
|
||||
// try {
|
||||
// $operateData = array_merge($data, ['member_id' => $memberId]);
|
||||
// $result = $this->operate($operateData);
|
||||
// $results[] = $result;
|
||||
// } catch (xception $e) {
|
||||
// $results[] = ['member_id' => $memberId, 'error' => $e->getMessage()];
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return $results;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取积分统计
|
||||
// */
|
||||
// public function getStatistics(array $params): array
|
||||
// {
|
||||
// $startTime = $params['start_time'] ?? null;
|
||||
// $endTime = $params['end_time'] ?? null;
|
||||
//
|
||||
// $where = [];
|
||||
// if ($startTime) {
|
||||
// $where[] = ['create_time', '>=', strtotime($startTime)];
|
||||
// }
|
||||
// if ($endTime) {
|
||||
// $where[] = ['create_time', '<=', strtotime($endTime) + 86399];
|
||||
// }
|
||||
//
|
||||
// $totalIncome = $this->dao->query()->where($where)->where('type', 1)->sum('points') ?: 0;
|
||||
// $totalExpense = $this->dao->query()->where($where)->where('type', 2)->sum('points') ?: 0;
|
||||
// $totalRecords = $this->dao->query()->where($where)->count();
|
||||
//
|
||||
// return [
|
||||
// 'total_income' => $totalIncome,
|
||||
// 'total_expense' => $totalExpense,
|
||||
// 'net_income' => $totalIncome - $totalExpense,
|
||||
// 'total_records' => $totalRecords,
|
||||
// ];
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 设置积分规则
|
||||
// */
|
||||
// public function setRules(array $data): array
|
||||
// {
|
||||
// $rules = [
|
||||
// 'sign_points' => $data['sign_points'] ?? 10,
|
||||
// 'login_points' => $data['login_points'] ?? 5,
|
||||
// 'max_sign_days' => $data['max_sign_days'] ?? 30,
|
||||
// ];
|
||||
//
|
||||
// $config = $this->configDao->getByKey('member_points_rules');
|
||||
// if ($config) {
|
||||
// $config->value = json_encode($rules);
|
||||
// $config->save();
|
||||
// } else {
|
||||
// $this->configDao->save([
|
||||
// 'key' => 'member_points_rules',
|
||||
// 'value' => json_encode($rules),
|
||||
// 'name' => '会员积分规则',
|
||||
// 'group' => 'member',
|
||||
// 'type' => 'json',
|
||||
// ]);
|
||||
// }
|
||||
//
|
||||
// return $rules;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取积分规则
|
||||
// */
|
||||
// public function getRules(): array
|
||||
// {
|
||||
// $config = $this->configDao->getByKey('member_points_rules');
|
||||
// if ($config) {
|
||||
// return json_decode($config->value, true) ?: [];
|
||||
// }
|
||||
//
|
||||
// // 默认规则
|
||||
// return [
|
||||
// 'sign_points' => 10,
|
||||
// 'login_points' => 5,
|
||||
// 'max_sign_days' => 30,
|
||||
// ];
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\service\admin\member;
|
||||
|
||||
use app\dao\member\MemberDao;
|
||||
use app\enum\member\PointSource;
|
||||
use app\enum\member\PointType;
|
||||
use app\adminapi\event\PointsChangedEvent;
|
||||
use core\base\BaseService;
|
||||
use core\exception\handler\AdminException;
|
||||
use support\Container;
|
||||
|
||||
/**
|
||||
* 会员服务类
|
||||
* @method getUsersListByTagId(mixed $where, mixed $field, mixed $page, mixed $limit)
|
||||
* @method getUsersExcludingTag(mixed $where, mixed $field, mixed $page, mixed $limit)
|
||||
*/
|
||||
class MemberService extends BaseService
|
||||
{
|
||||
/**
|
||||
* 构造方法
|
||||
*/
|
||||
public function __construct(MemberDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配标签
|
||||
*
|
||||
* @throws \core\exception\handler\AdminException
|
||||
*/
|
||||
public function assignTags(int|string $id, array $tags = []): void
|
||||
{
|
||||
try {
|
||||
$model = $this->dao->get($id);
|
||||
if (empty($model)) {
|
||||
throw new AdminException('会员不存在');
|
||||
}
|
||||
$model->tags()->sync($tags);
|
||||
} catch (\Exception $exception) {
|
||||
throw new AdminException($exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整会员积分(使用 Laravel ORM)
|
||||
*
|
||||
* @param int|string $id 会员ID
|
||||
* @param int $points 积分变动值(正数增加,负数减少)
|
||||
* @param array $options
|
||||
*
|
||||
* @throws \Throwable
|
||||
* @throws \core\exception\handler\AdminException
|
||||
*/
|
||||
public function adjustPoints(int|string $id, int $points, array $options = []): void
|
||||
{
|
||||
try {
|
||||
$this->transaction(function () use ($id, $points, $options) {
|
||||
// 使用 Laravel ORM 查询会员
|
||||
$member = $this->dao->get($id);
|
||||
if (!$member) {
|
||||
throw new AdminException('会员不存在');
|
||||
}
|
||||
// 检查积分是否足够(如果是减少积分)
|
||||
if ($points < 0 && $member->points < abs($points)) {
|
||||
throw new AdminException('会员积分不足');
|
||||
}
|
||||
|
||||
// 计算新积分
|
||||
$newPoints = abs($points);
|
||||
// 积分不能小于0
|
||||
if ($newPoints < 0) {
|
||||
throw new AdminException('积分不能小于0');
|
||||
}
|
||||
//添加积分记录
|
||||
$pointsService = Container::make(MemberPointsService::class);
|
||||
$params = [
|
||||
'member_id' => $member->id,
|
||||
'points' => $points,
|
||||
'balance' => $newPoints,
|
||||
'type' => $options['type'] ?? PointType::ADJUST->value,
|
||||
'source' => $options['source'] ?? PointSource::ADMIN->value,
|
||||
'remark' => $options['remark'] ?? '',
|
||||
];
|
||||
$pointsService->save($params);
|
||||
$member->update(['points' => $newPoints]);
|
||||
|
||||
$source = PointSource::ADMIN;
|
||||
if (!empty($params['source'])) {
|
||||
$source = PointSource::tryFrom($params['source']) ?? PointSource::ADMIN;
|
||||
}
|
||||
|
||||
$event = new PointsChangedEvent(
|
||||
$member->id,
|
||||
$points,
|
||||
$source,
|
||||
PointType::INCREASE,
|
||||
$member->points,
|
||||
$newPoints,
|
||||
$options['remark'] ?? ''
|
||||
);
|
||||
$event->dispatch();
|
||||
});
|
||||
} catch (\Throwable $exception) {
|
||||
throw new AdminException('调整积分失败: ' . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\service\admin\member;
|
||||
|
||||
use app\dao\member\MemberSignDao;
|
||||
use core\base\BaseService;
|
||||
|
||||
/**
|
||||
* 会员签到服务类
|
||||
*/
|
||||
class MemberSignService extends BaseService
|
||||
{
|
||||
/**
|
||||
* 构造方法
|
||||
*/
|
||||
public function __construct(MemberSignDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员签到统计
|
||||
*/
|
||||
public function getMemberSignStatistics(int $memberId): array
|
||||
{
|
||||
return $this->dao->getSignStatistics($memberId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员签到记录
|
||||
*/
|
||||
public function getMemberSignRecords(int $memberId, int $days = 30): array
|
||||
{
|
||||
return $this->dao->getMemberSignRecords($memberId, $days);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动为会员签到
|
||||
*/
|
||||
public function manualSign(int $memberId, int $points = 10): array
|
||||
{
|
||||
return $this->dao->create($memberId, date('Y-m-d'), $points, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取签到日历数据
|
||||
*/
|
||||
public function getSignCalendar(int $memberId, string $yearMonth): array
|
||||
{
|
||||
$year = substr($yearMonth, 0, 4);
|
||||
$month = substr($yearMonth, 5, 2);
|
||||
|
||||
$startDate = "{$year}-{$month}-01";
|
||||
$endDate = date('Y-m-t', strtotime($startDate));
|
||||
|
||||
$signRecords = $this->dao->where('member_id', $memberId)
|
||||
->whereBetween('sign_date', [$startDate, $endDate])
|
||||
->pluck('sign_date')
|
||||
->toArray();
|
||||
|
||||
$calendar = [];
|
||||
$currentDate = $startDate;
|
||||
|
||||
while ($currentDate <= $endDate) {
|
||||
$calendar[] = [
|
||||
'date' => $currentDate,
|
||||
'signed' => in_array($currentDate, $signRecords),
|
||||
'day' => date('d', strtotime($currentDate))
|
||||
];
|
||||
$currentDate = date('Y-m-d', strtotime($currentDate . ' +1 day'));
|
||||
}
|
||||
|
||||
return $calendar;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\service\admin\member;
|
||||
|
||||
use app\dao\member\MemberTagPermissionDao;
|
||||
use core\base\BaseService;
|
||||
use core\exception\handler\AdminException;
|
||||
use support\Container;
|
||||
|
||||
/**
|
||||
* 会员标签权限服务类
|
||||
*/
|
||||
class MemberTagPermissionService extends BaseService
|
||||
{
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
*/
|
||||
public function __construct(MemberTagPermissionDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新设置角色权限(含事务封装)
|
||||
*
|
||||
* @param $data
|
||||
*
|
||||
* @throws \core\exception\handler\AdminException
|
||||
*/
|
||||
public function save($data): void
|
||||
{
|
||||
try {
|
||||
$this->transaction(function () use ($data) {
|
||||
$this->saveWithoutTransaction($data);
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
throw new AdminException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 无事务版本:核心业务逻辑(供内部/外部事务复用)
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @throws \core\exception\handler\AdminException
|
||||
*/
|
||||
public function saveWithoutTransaction(array $data): void
|
||||
{
|
||||
try {
|
||||
$tagId = $data['tag_id'] ?? '';
|
||||
$newPermissions = $data['menu_id'] ?? [];
|
||||
if (empty($tagId)) {
|
||||
throw new AdminException('参数错误缺少tag_id', ['errorCode' => -1]);
|
||||
}
|
||||
// 1. 获取会员标签模型(确保会员标签存在)
|
||||
/** @var MemberTagService $tagService */
|
||||
$tagService = Container::make(MemberTagService::class);
|
||||
$tagModel = $tagService->get($tagId, ['*'], ['permissions']);
|
||||
if (!$tagModel) {
|
||||
throw new AdminException('会员标签不存在', ['errorCode' => -1]);
|
||||
}
|
||||
$tagModel->permissions()->sync($newPermissions);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
throw new AdminException($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\service\admin\member;
|
||||
|
||||
use app\dao\member\MemberTagRelationDao;
|
||||
use core\base\BaseService;
|
||||
use core\exception\handler\AdminException;
|
||||
|
||||
|
||||
class MemberTagRelationService extends BaseService
|
||||
{
|
||||
public function __construct(MemberTagRelationDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除会员用户-关联标签
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @throws \core\exception\handler\AdminException
|
||||
*/
|
||||
public function removeUserTag(array $data): void
|
||||
{
|
||||
try {
|
||||
$this->transaction(function () use ($data) {
|
||||
foreach ($data as $item) {
|
||||
$this->dao->getModel()->where($item)->delete();
|
||||
}
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
throw new AdminException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存用户-关联标签
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array 返回保存结果 ['saved' => int, 'duplicates' => int, 'failed' => int]
|
||||
* @throws \core\exception\handler\AdminException
|
||||
*/
|
||||
public function saveUserTags(array $data): array
|
||||
{
|
||||
try {
|
||||
return $this->transaction(function () use ($data) {
|
||||
// 检查数据有效性
|
||||
if (empty($data)) {
|
||||
throw new AdminException('数据不能为空');
|
||||
}
|
||||
// 检查必填字段
|
||||
foreach ($data as $index => $item) {
|
||||
if (!isset($item['member_id']) || !isset($item['tag_id'])) {
|
||||
throw new AdminException("数据格式错误: 第 {$index} 条记录缺少 member_id 或 tag_id");
|
||||
}
|
||||
}
|
||||
// 使用专门的批量保存方法
|
||||
$result = $this->dao->batchSave($data);
|
||||
// 如果没有任何数据被保存且没有重复记录,则认为是失败
|
||||
if ($result['saved'] === 0 && $result['duplicates'] === 0 && $result['failed'] > 0) {
|
||||
throw new AdminException('批量保存失败: ' . implode(', ', $result['errors']));
|
||||
}
|
||||
return $result;
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
throw new AdminException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\service\admin\member;
|
||||
|
||||
use app\dao\member\MemberDao;
|
||||
use app\dao\member\MemberTagDao;
|
||||
use app\dao\member\MemberTagRelationDao;
|
||||
use app\model\web\Menu;
|
||||
use app\model\member\MemberTag;
|
||||
use core\base\BaseService;
|
||||
use core\exception\handler\AdminException;
|
||||
use support\Container;
|
||||
|
||||
/**
|
||||
* 会员标签服务类
|
||||
*/
|
||||
class MemberTagService extends BaseService
|
||||
{
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
*/
|
||||
public function __construct(MemberTagDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为会员添加标签
|
||||
*/
|
||||
public function addTagToMember(int $memberId, int $tagId): bool
|
||||
{
|
||||
/** @var MemberTagRelationDao $dao */
|
||||
$dao = Container::make(MemberTagRelationDao::class);
|
||||
return $dao->addTagRelation($memberId, $tagId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为会员移除标签
|
||||
*/
|
||||
public function removeTagFromMember(int $memberId, int $tagId): bool
|
||||
{
|
||||
/** @var MemberTagRelationDao $dao */
|
||||
$dao = Container::make(MemberTagRelationDao::class);
|
||||
return $dao->removeTagRelation($memberId, $tagId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标签下的会员列表
|
||||
*/
|
||||
public function getTagMembers(int $tagId, array $params): array
|
||||
{
|
||||
$tag = $this->dao->get($tagId);
|
||||
if (!$tag) {
|
||||
throw new AdminException('标签不存在');
|
||||
}
|
||||
|
||||
$page = (int)($params['page'] ?? 1);
|
||||
$limit = (int)($params['limit'] ?? 10);
|
||||
|
||||
/** @var MemberTagRelationDao $relationDao */
|
||||
$relationDao = Container::make(MemberTagRelationDao::class);
|
||||
/** @var MemberDao $memberDao */
|
||||
$memberDao = Container::make(MemberDao::class);
|
||||
|
||||
// 获取标签关联的会员ID
|
||||
$relations = $relationDao->selectList(
|
||||
[['tag_id', '=', $tagId]],
|
||||
'member_id',
|
||||
0, 10000
|
||||
);
|
||||
$memberIds = $relations->pluck('member_id')->toArray();
|
||||
|
||||
if (empty($memberIds)) {
|
||||
return ['total' => 0, 'list' => []];
|
||||
}
|
||||
|
||||
$where = [['id', 'in', $memberIds]];
|
||||
$field = '*';
|
||||
$order = 'create_time desc';
|
||||
|
||||
[$total, $list] = $memberDao->getList($where, $field, $page, $limit, $order);
|
||||
|
||||
return compact('total', 'list');
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量分配标签
|
||||
*/
|
||||
public function batchAssignTags(array $data): array
|
||||
{
|
||||
$memberIds = $data['member_ids'] ?? [];
|
||||
$tagIds = $data['tag_ids'] ?? [];
|
||||
$action = $data['action'] ?? 'assign';
|
||||
|
||||
if (empty($memberIds)) {
|
||||
throw new AdminException('请选择会员');
|
||||
}
|
||||
|
||||
if (empty($tagIds)) {
|
||||
throw new AdminException('请选择标签');
|
||||
}
|
||||
|
||||
/** @var MemberTagRelationDao $relationDao */
|
||||
$relationDao = Container::make(MemberTagRelationDao::class);
|
||||
|
||||
$relationDao->getModel()->getConnection()->beginTransaction();
|
||||
try {
|
||||
foreach ($memberIds as $memberId) {
|
||||
if ($action == 'assign') {
|
||||
foreach ($tagIds as $tagId) {
|
||||
$this->addTagToMember($memberId, $tagId);
|
||||
}
|
||||
} else if ($action == 'remove') {
|
||||
foreach ($tagIds as $tagId) {
|
||||
$this->removeTagFromMember($memberId, $tagId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$relationDao->getModel()->getConnection()->commit();
|
||||
return ['success' => true, 'count' => count($memberIds)];
|
||||
} catch (\Exception $e) {
|
||||
$relationDao->getModel()->getConnection()->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有启用的标签
|
||||
*/
|
||||
public function getEnabledTags(): array
|
||||
{
|
||||
$where = [['status', '=', 1]];
|
||||
$list = $this->dao->selectList($where, '*', 0, 1000, 'sort asc');
|
||||
return $list->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新标签排序
|
||||
*/
|
||||
public function updateSort(int $id, int $sort): array
|
||||
{
|
||||
$tag = $this->dao->get($id);
|
||||
if (!$tag) {
|
||||
throw new AdminException('标签不存在');
|
||||
}
|
||||
|
||||
$tag->sort = $sort;
|
||||
$tag->save();
|
||||
|
||||
return $tag->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 为标签分配权限
|
||||
*/
|
||||
public function assignPermissions(int $tagId, array $permissionCodes): bool
|
||||
{
|
||||
// 验证标签存在
|
||||
$tag = $this->dao->get($tagId);
|
||||
if (!$tag) {
|
||||
throw new AdminException('标签不存在');
|
||||
}
|
||||
|
||||
// 验证权限代码是否存在于 web_menu 表中(code 不为空)
|
||||
if (!empty($permissionCodes)) {
|
||||
$validCodes = Menu::whereIn('code', $permissionCodes)
|
||||
->whereNotNull('code')
|
||||
->where('code', '<>', '')
|
||||
->where('enabled', 1)
|
||||
->pluck('code')
|
||||
->toArray();
|
||||
|
||||
if (count($validCodes) !== count($permissionCodes)) {
|
||||
$invalidCodes = array_diff($permissionCodes, $validCodes);
|
||||
throw new AdminException('以下权限码不存在或已禁用: ' . implode(', ', $invalidCodes));
|
||||
}
|
||||
}
|
||||
|
||||
// 使用 belongsToMany 同步权限(标准 pivot 操作)
|
||||
$tagModel = MemberTag::find($tagId);
|
||||
if (!$tagModel) {
|
||||
throw new AdminException('标签不存在');
|
||||
}
|
||||
|
||||
$tagModel->permissions()->sync($validCodes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员的所有权限
|
||||
*/
|
||||
public function getMemberPermissions(int $memberId): array
|
||||
{
|
||||
$member = MemberDao::make()->get($memberId);
|
||||
if (!$member) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 查询会员的所有启用标签及权限
|
||||
$tags = MemberTag::with(['permissions'])
|
||||
->whereHas('members', function ($query) use ($memberId) {
|
||||
$query->where('member_id', $memberId);
|
||||
})
|
||||
->where('enabled', 1)
|
||||
->get();
|
||||
|
||||
if ($tags->isEmpty()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 收集所有权限
|
||||
$permissions = [];
|
||||
foreach ($tags as $tag) {
|
||||
foreach ($tag->permissions as $permission) {
|
||||
$permissions[] = $permission->code;
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有启用的标签(含权限)
|
||||
*/
|
||||
public function getEnabledTagsWithPermissions(): array
|
||||
{
|
||||
return MemberTag::with('permissions')
|
||||
->where('enabled', 1)
|
||||
->orderBy('sort')
|
||||
->get()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有可用的权限码(从 web_menu 表)
|
||||
*/
|
||||
public function getAvailablePermissions(): array
|
||||
{
|
||||
return Menu::whereNotNull('code')
|
||||
->where('code', '<>', '')
|
||||
->where('enabled', 1)
|
||||
->orderBy('sort')
|
||||
->get(['id', 'code', 'name', 'url', 'category'])
|
||||
->toArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\service\admin\member;
|
||||
|
||||
use app\dao\member\MemberWithdrawDao;
|
||||
use app\enum\member\WithdrawStatus;
|
||||
use core\base\BaseService;
|
||||
|
||||
/**
|
||||
* 提现管理服务
|
||||
*/
|
||||
class MemberWithdrawAdminService extends BaseService
|
||||
{
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct(MemberWithdrawDao $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待审核提现列表
|
||||
*/
|
||||
public function getPendingWithdraws(array $params = [])
|
||||
{
|
||||
return $this->dao->getPendingWithdraws($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核提现
|
||||
*/
|
||||
public function auditWithdraw(int $withdrawId, int $status, string $auditRemark = '')
|
||||
{
|
||||
$withdraw = $this->dao->find($withdrawId);
|
||||
if (!$withdraw) {
|
||||
throw new \Exception('提现记录不存在');
|
||||
}
|
||||
|
||||
if (!in_array($status, [WithdrawStatus::APPROVED->value, WithdrawStatus::REJECTED->value])) {
|
||||
throw new \Exception('审核状态无效');
|
||||
}
|
||||
|
||||
return $this->dao->updateStatus($withdraw, $status, $auditRemark);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提现记录详情
|
||||
*/
|
||||
public function getWithdrawDetail(int $withdrawId)
|
||||
{
|
||||
$withdraw = $this->dao->find($withdrawId);
|
||||
if (!$withdraw) {
|
||||
throw new \Exception('提现记录不存在');
|
||||
}
|
||||
|
||||
return $withdraw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新提现状态
|
||||
*/
|
||||
public function updateWithdrawStatus(int $withdrawId, int $status)
|
||||
{
|
||||
$withdraw = $this->dao->find($withdrawId);
|
||||
if (!$withdraw) {
|
||||
throw new \Exception('提现记录不存在');
|
||||
}
|
||||
|
||||
return $this->dao->updateStatus($withdraw, $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提现统计
|
||||
*/
|
||||
public function getWithdrawStats(array $params = [])
|
||||
{
|
||||
$totalApply = $this->dao->getCount($params);
|
||||
$totalSuccess = $this->dao->getCount(array_merge($params, ['status' => WithdrawStatus::COMPLETED->value]));
|
||||
$totalRejected = $this->dao->getCount(array_merge($params, ['status' => WithdrawStatus::REJECTED->value]));
|
||||
|
||||
return [
|
||||
'total_apply' => $totalApply,
|
||||
'total_success' => $totalSuccess,
|
||||
'total_rejected' => $totalRejected,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user