From 17afcf4d325891b1206cb978a1260232c3c14e03 Mon Sep 17 00:00:00 2001 From: "zhangf@suq.cn" Date: Thu, 18 Dec 2025 10:04:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin):=20=E6=96=B0=E5=A2=9E=E7=82=B9?= =?UTF-8?q?=E6=95=B0=E6=8A=B5=E6=89=A3=E9=85=8D=E7=BD=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加点数抵扣配置控制器、模型、验证器及服务层 - 配置点数抵扣相关路由并设置权限控制 - 实现点数抵扣中间件,支持请求前扣除点数及失败后返还逻辑 - 新增用户套餐与点数日志关联模型及数据访问对象 - 在用户端路由中增加用户信息及点数记录查询接口 - 扩展用户服务,支持获取包含套餐信息的用户详情 --- .../admin/PointDeductConfigController.php | 59 +++++++++ app/controller/user/UserController.php | 13 ++ app/controller/user/UserPointLogController.php | 18 +++ app/dao/admin/PointDeductConfigDao.php | 15 +++ app/dao/user/UserPackageDao.php | 15 +++ app/dao/user/UserPointLogDao.php | 15 +++ app/middleware/PointDeductMiddleware.php | 145 +++++++++++++++++++++ app/model/admin/PointDeductConfig.php | 33 +++++ app/model/user/UserPackage.php | 33 +++++ app/model/user/UserPointLog.php | 33 +++++ app/route/admin.php | 10 ++ app/route/route.php | 14 +- app/service/admin/PointDeductConfigService.php | 114 ++++++++++++++++ app/service/user/UserPointLogService.php | 43 ++++++ app/validate/PointDeductConfigValidate.php | 36 +++++ plugin/piadmin/app/model/User.php | 6 + plugin/piadmin/app/service/UserService.php | 11 ++ 17 files changed, 611 insertions(+), 2 deletions(-) create mode 100644 app/controller/admin/PointDeductConfigController.php create mode 100644 app/controller/user/UserController.php create mode 100644 app/controller/user/UserPointLogController.php create mode 100644 app/dao/admin/PointDeductConfigDao.php create mode 100644 app/dao/user/UserPackageDao.php create mode 100644 app/dao/user/UserPointLogDao.php create mode 100644 app/middleware/PointDeductMiddleware.php create mode 100644 app/model/admin/PointDeductConfig.php create mode 100644 app/model/user/UserPackage.php create mode 100644 app/model/user/UserPointLog.php create mode 100644 app/service/admin/PointDeductConfigService.php create mode 100644 app/service/user/UserPointLogService.php create mode 100644 app/validate/PointDeductConfigValidate.php diff --git a/app/controller/admin/PointDeductConfigController.php b/app/controller/admin/PointDeductConfigController.php new file mode 100644 index 0000000..c18c27e --- /dev/null +++ b/app/controller/admin/PointDeductConfigController.php @@ -0,0 +1,59 @@ + '', + 'menu_id' => '', + 'route' => '', + 'deduct' => '', + 'status' => '' + ]); + validate(PointDeductConfigValidate::class)->check($params, 'save'); + return success($service->saveData($params)); + } + + public function update(PointDeductConfigService $service): Response + { + $params = requestOnly([ + 'id' => '', + 'name' => '', + 'menu_id' => '', + 'route' => '', + 'deduct' => '', + 'status' => '' + ]); + validate(PointDeductConfigValidate::class)->check($params, 'update'); + return success($service->updateData(ArrayUtils::filterNotEmpty($params))); + } + + public function index(PointDeductConfigService $service): Response + { + $params = requestOnly([ + 'name' => '', + 'route' => '', + ]); + return success($service->listData($params)); + } + + public function read(PointDeductConfigService $service): Response + { + $id = input('id'); + return success($service->readData($id)); + } + + public function delete(PointDeductConfigService $service): Response + { + $ids = input('ids'); + return success($service->deleteData($ids)); + } + +} \ No newline at end of file diff --git a/app/controller/user/UserController.php b/app/controller/user/UserController.php new file mode 100644 index 0000000..89ea725 --- /dev/null +++ b/app/controller/user/UserController.php @@ -0,0 +1,13 @@ +getUserInfoWithPackage()); + } + +} \ No newline at end of file diff --git a/app/controller/user/UserPointLogController.php b/app/controller/user/UserPointLogController.php new file mode 100644 index 0000000..c28cc25 --- /dev/null +++ b/app/controller/user/UserPointLogController.php @@ -0,0 +1,18 @@ + '', + 'end_time' => '' + ]); + return success($service->listData($params)); + } + +} \ No newline at end of file diff --git a/app/dao/admin/PointDeductConfigDao.php b/app/dao/admin/PointDeductConfigDao.php new file mode 100644 index 0000000..ffb4da4 --- /dev/null +++ b/app/dao/admin/PointDeductConfigDao.php @@ -0,0 +1,15 @@ +route->param('perm')[0]; + $pointDeductDao = app()->make(PointDeductConfigDao::class); + $config = $pointDeductDao->getOne(['route' => $requestRoute]); + //生成唯一值 + $code = uniqid(); + if ($config) { + //存在,扣除积分 + $this->deductPoint($config, $code); + } else { + //不存在继续 + return $handler($request); + } + $response = $handler($request); + $responseData = json_decode($response->rawBody(), true) ?: []; + if ($responseData['code'] != 0) { + //失败则返还积分 + $this->refund($code); + } + Cache::delete($code); + return $response; + } + + /** + * 扣除积分 + */ + private function deductPoint($config, $code) + { + $userInfo = RequestUtils::getUserInfo(); + $userPackageDao = app()->make(UserPackageDao::class); + // 开启事务 + Db::beginTransaction(); + try { + // 锁定并查询用户套餐 + $userPackage = $userPackageDao->where([ + 'uid' => $userInfo['id'], + ['end_time', '>', time()] + ])->lock(true)->find(); + + if (!$userPackage || $userPackage['point'] < $config['deduct']) { + throw new ApiException('用户点数不足'); + } + + // 更新点数 + $result = $userPackageDao->update($userPackage['id'], [ + 'point' => $userPackage['point'] - $config['deduct'] + ]); + + if (!$result) { + throw new ApiException('扣点失败'); + } + //记录日志 + $userPointLog = app()->make(UserPointLogDao::class); + $log = $userPointLog->save([ + 'uid' => $userInfo['id'], + 'point' => $config['deduct'], + 'desc' => $config['name'] . '扣除点数', + 'code' => $config['code'], + 'balance' => $result['point'] + ]); + Cache::set($code, $log['id']); + // 提交事务 + Db::commit(); + } catch (\Exception $e) { + // 回滚事务 + Db::rollback(); + throw $e; + } + } + + private function refund($code) + { + $log_id = Cache::get($code); + $userInfo = RequestUtils::getUserInfo(); + $userPackageDao = app()->make(UserPackageDao::class); + // 开启事务 + Db::beginTransaction(); + try { + // 锁定并查询用户套餐 + $userPackage = $userPackageDao->where([ + 'uid' => $userInfo['id'], + ['end_time', '>', time()] + ])->lock(true)->find(); + + $userPointLog = app()->make(UserPointLogDao::class); + $log = $userPointLog->getOne([ + 'id' => $log_id + ]); + if (!$log) { + throw new ApiException('日志不存在'); + } + // 返还点数 + $result = $userPackageDao->update($userPackage['id'], [ + 'point' => $userPackage['point'] + $log['point'] + ]); + if (!$result) { + throw new ApiException('点数返还失败,剩余点数:' . $result['point']); + } + //更新日志 + $userPointLog->update([ + 'uid' => $userInfo['id'], + 'id' => $log_id, + ], [ + 'msg' => '失败,点数已返还,剩余点数:' . $result['point'], + 'status' => 2, + 'balance' => $log['balance'] + $log['point'] + ]); + // 提交事务 + Db::commit(); + } catch (\Exception $e) { + // 回滚事务 + Db::rollBack(); + throw $e; + } + } +} diff --git a/app/model/admin/PointDeductConfig.php b/app/model/admin/PointDeductConfig.php new file mode 100644 index 0000000..0d3d868 --- /dev/null +++ b/app/model/admin/PointDeductConfig.php @@ -0,0 +1,33 @@ +setParams(['perm' => 'packageIndex']); }); + //扣除点数配置 + Route::group('/pointDeduct', function () { + Route::post('/save', [PointDeductConfigController::class, 'save'])->setParams(['perm' => ['pointDeductSave']]); + Route::post('/update', [PointDeductConfigController::class, 'update'])->setParams(['perm' => ['pointDeductUpdate']]); + Route::get('/index', [PointDeductConfigController::class, 'index'])->setParams(['perm' => ['pointDeductIndex']]); + Route::get('/read', [PointDeductConfigController::class, 'read'])->setParams(['perm' => ['pointDeductRead']]); + Route::post('/delete', [PointDeductConfigController::class, 'delete'])->setParams(['perm' => ['pointDeductDelete']]); + }); + })->middleware([ AdminAuthorizationMiddleware::class, PermissionsMiddleware::class diff --git a/app/route/route.php b/app/route/route.php index eeba690..f58363a 100644 --- a/app/route/route.php +++ b/app/route/route.php @@ -16,6 +16,9 @@ use app\controller\user\ExpiosiveReplicaController; use app\controller\user\GlobalProxyController; use app\controller\user\IndexController; use app\controller\user\KnowledgeLibraryController; +use app\controller\user\UserController; +use app\controller\user\UserPointLogController; +use app\middleware\PointDeductMiddleware; use plugin\piadmin\app\middleware\UserAuthorizationMiddleware; use Webman\Route; @@ -23,6 +26,13 @@ use Webman\Route; * 用户端定制路由 */ Route::group('/service/v1', function () { + Route::group('/user', function () { + //用户信息 + Route::get('/info', [UserController::class, 'userInfo'])->setParams(['perm' => ['userInfo']]); + //用户点数记录 + Route::get('/pointLog/index', [UserPointLogController::class, 'index'])->setParams(['perm' => ['userPointLogIndex']]); + }); + //数据看板 Route::group('/databoard', function () { //数据总量 @@ -216,8 +226,8 @@ Route::group('/service/v1', function () { //详情 Route::get('/read', [GlobalProxyController::class, 'read'])->setParams(['perm' => ['proxyRead']]); }); + })->middleware([ UserAuthorizationMiddleware::class, -// AdminAuthorizationMiddleware::class, -// PermissionsMiddleware::class + PointDeductMiddleware::class ]); \ No newline at end of file diff --git a/app/service/admin/PointDeductConfigService.php b/app/service/admin/PointDeductConfigService.php new file mode 100644 index 0000000..e9d4ff6 --- /dev/null +++ b/app/service/admin/PointDeductConfigService.php @@ -0,0 +1,114 @@ +dao = $dao; + } + + /** + * 保存信息 + * @param array $params + * @return array + */ + public function saveData(array $params): array + { + Db::startTrans(); + try { + $data = $this->dao->save($params); + Db::commit(); + } catch (\Exception $exception) { + Db::rollback(); + throw new ApiException($exception->getMessage()); + } + return $data->toArray(); + } + + /** + * 修改信息 + * @param array $params + * @return array + */ + public function updateData(array $params): array + { + // 落库 + Db::startTrans(); + try { + $this->dao->update(['id' => $params['id']], $params); + Db::commit(); + } catch (\Exception $exception) { + Db::rollback(); + throw new ApiException($exception->getMessage()); + } + return $params; + } + + /** + * 获取列表 + * @param array $params + * @return array + */ + public function listData(array $params): array + { + [$page, $limit] = RequestUtils::getPageParameter(); + [$sortRule, $sortField] = RequestUtils::getSortParameter(); + $query = [ + 'delete_time' => 0 + ]; + if (isNotBlank($params['name'])) { + $query[] = ['name', 'like', '%' . $params['name'] . '%']; + } + if (isNotBlank($params['route'])) { + $query[] = ['route', '=', $params['route']]; + } + $list = $this->dao->getList($query, '*', $page, $limit, "$sortField $sortRule"); + $count = $this->dao->getCount($query); + return compact('list', 'count'); + } + + /** + * 获取信息 + * @param mixed $id + * @return array + */ + public function readData(mixed $id): array + { + $package = $this->dao->get(['id' => $id]); + if (empty($package)) { + throw new ApiException('数据不存在'); + } + return $package->toArray(); + } + + /** + * 删除信息 + * @param mixed $id + * @return array + */ + public function deleteData(array $ids): array + { + // 落库 + Db::startTrans(); + try { + $this->dao->update([['id', 'in', $ids]], ['delete_time' => time()]); + Db::commit(); + } catch (\Exception $exception) { + Db::rollback(); + throw new ApiException($exception->getMessage()); + } + return ['id' => $ids]; + } + +} \ No newline at end of file diff --git a/app/service/user/UserPointLogService.php b/app/service/user/UserPointLogService.php new file mode 100644 index 0000000..9d79c23 --- /dev/null +++ b/app/service/user/UserPointLogService.php @@ -0,0 +1,43 @@ +dao = $dao; + } + + + /** + * 获取列表 + * @param array $params + * @return array + */ + public function listData(array $params): array + { + [$page, $limit] = RequestUtils::getPageParameter(); + [$sortRule, $sortField] = RequestUtils::getSortParameter(); + $query = [ + 'delete_time' => 0 + ]; + if (isNotBlank($params['begin_time'])) { + $query[] = ['create_time', '>=', strtotime($params['begin_time'])]; + } + if (isNotBlank($params['end_time'])) { + $query[] = ['create_time', '<=', strtotime($params['end_time'] . ' 23:59:59')]; + } + $list = $this->dao->getList($query, '*', $page, $limit, "$sortField $sortRule"); + $count = $this->dao->getCount($query); + return compact('list', 'count'); + } + +} \ No newline at end of file diff --git a/app/validate/PointDeductConfigValidate.php b/app/validate/PointDeductConfigValidate.php new file mode 100644 index 0000000..67b0096 --- /dev/null +++ b/app/validate/PointDeductConfigValidate.php @@ -0,0 +1,36 @@ + [ + 'menu_id' => 'require|number', + 'name' => 'require', + 'route' => 'require', + 'deduct' => 'require|number', + 'status' => 'require|number' + ], + 'update' => [ + 'id' => 'require', + 'name' => 'require', + 'menu_id' => 'require|number', + 'route' => 'require', + 'deduct' => 'require|number', + 'status' => 'require|number' + ], + ]; + protected $message = [ + 'id.require' => 'ID不能为空', + 'menu_id.require' => '菜单ID不能为空', + 'menu_id.number' => '菜单ID只能为数字', + 'route.require' => '路由不能为空', + 'deduct.require' => '扣除点数不能为空', + 'deduct.number' => '扣除点数只能为数字', + 'status.require' => '状态不能为空', + 'status.number' => '状态只能为数字', + ]; +} \ No newline at end of file diff --git a/plugin/piadmin/app/model/User.php b/plugin/piadmin/app/model/User.php index 77ff0ff..501a032 100644 --- a/plugin/piadmin/app/model/User.php +++ b/plugin/piadmin/app/model/User.php @@ -2,6 +2,7 @@ namespace plugin\piadmin\app\model; +use app\model\user\UserPackage; use plugin\piadmin\app\base\BaseModel; use think\model\concern\SoftDelete; @@ -18,4 +19,9 @@ class User extends BaseModel protected $append = []; + public function package() + { + return $this->hasOne(UserPackage::class, 'uid', 'id'); + } + } \ No newline at end of file diff --git a/plugin/piadmin/app/service/UserService.php b/plugin/piadmin/app/service/UserService.php index ecd731f..27b5e42 100644 --- a/plugin/piadmin/app/service/UserService.php +++ b/plugin/piadmin/app/service/UserService.php @@ -235,6 +235,17 @@ class UserService extends BaseService 'status' => $status ]; } + + /** + * 用户端获取用户信息 + * 包含套餐信息 + */ + public function getUserInfoWithPackage() + { + $user = RequestUtils::getUserInfo(); + $userinfo = $this->dao->get(['id' => $user['id']], ['*'], ['package']); + return $userinfo; + } // ============================================================ 私有方法 ===============================================