Browse Source

feat(admin): 新增点数抵扣配置功能

- 添加点数抵扣配置控制器、模型、验证器及服务层
- 配置点数抵扣相关路由并设置权限控制
- 实现点数抵扣中间件,支持请求前扣除点数及失败后返还逻辑
- 新增用户套餐与点数日志关联模型及数据访问对象
- 在用户端路由中增加用户信息及点数记录查询接口
- 扩展用户服务,支持获取包含套餐信息的用户详情
master
zhangf@suq.cn 22 hours ago
parent
commit
17afcf4d32
  1. 59
      app/controller/admin/PointDeductConfigController.php
  2. 13
      app/controller/user/UserController.php
  3. 18
      app/controller/user/UserPointLogController.php
  4. 15
      app/dao/admin/PointDeductConfigDao.php
  5. 15
      app/dao/user/UserPackageDao.php
  6. 15
      app/dao/user/UserPointLogDao.php
  7. 145
      app/middleware/PointDeductMiddleware.php
  8. 33
      app/model/admin/PointDeductConfig.php
  9. 33
      app/model/user/UserPackage.php
  10. 33
      app/model/user/UserPointLog.php
  11. 10
      app/route/admin.php
  12. 14
      app/route/route.php
  13. 114
      app/service/admin/PointDeductConfigService.php
  14. 43
      app/service/user/UserPointLogService.php
  15. 36
      app/validate/PointDeductConfigValidate.php
  16. 6
      plugin/piadmin/app/model/User.php
  17. 11
      plugin/piadmin/app/service/UserService.php

59
app/controller/admin/PointDeductConfigController.php

@ -0,0 +1,59 @@
<?php
namespace app\controller\admin;
use app\service\admin\PointDeductConfigService;
use app\validate\PointDeductConfigValidate;
use plugin\piadmin\app\utils\ArrayUtils;
use support\Response;
class PointDeductConfigController
{
public function save(PointDeductConfigService $service): Response
{
$params = requestOnly([
'name' => '',
'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));
}
}

13
app/controller/user/UserController.php

@ -0,0 +1,13 @@
<?php
namespace app\controller\user;
use plugin\piadmin\app\service\UserService;
class UserController
{
public function userInfo(UserService $service)
{
return success($service->getUserInfoWithPackage());
}
}

18
app/controller/user/UserPointLogController.php

@ -0,0 +1,18 @@
<?php
namespace app\controller\user;
use app\service\user\UserPointLogService;
use support\Response;
class UserPointLogController
{
public function index(UserPointLogService $service): Response
{
$params = requestOnly([
'begin_time' => '',
'end_time' => ''
]);
return success($service->listData($params));
}
}

15
app/dao/admin/PointDeductConfigDao.php

@ -0,0 +1,15 @@
<?php
namespace app\dao\admin;
use app\model\admin\PointDeductConfig;
use plugin\piadmin\app\base\BaseDao;
class PointDeductConfigDao extends BaseDao
{
protected function setModel(): string
{
return PointDeductConfig::class;
}
}

15
app/dao/user/UserPackageDao.php

@ -0,0 +1,15 @@
<?php
namespace app\dao\user;
use app\model\user\UserPackage;
use plugin\piadmin\app\base\UserBaseDao;
class UserPackageDao extends UserBaseDao
{
protected function setModel(): string
{
return UserPackage::class;
}
}

15
app/dao/user/UserPointLogDao.php

@ -0,0 +1,15 @@
<?php
namespace app\dao\user;
use app\model\user\UserPointLog;
use plugin\piadmin\app\base\UserBaseDao;
class UserPointLogDao extends UserBaseDao
{
protected function setModel(): string
{
return UserPointLog::class;
}
}

145
app/middleware/PointDeductMiddleware.php

@ -0,0 +1,145 @@
<?php
namespace app\middleware;
use app\dao\admin\PointDeductConfigDao;
use app\dao\user\UserPackageDao;
use app\dao\user\UserPointLogDao;
use plugin\piadmin\app\exception\ApiException;
use plugin\piadmin\app\utils\RequestUtils;
use support\Cache;
use support\Db;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;
/**
* 点数抵扣
*/
class PointDeductMiddleware implements MiddlewareInterface
{
/**
* @throws ModelNotFoundException
* @throws DataNotFoundException
* @throws DbException
*/
public function process(Request $request, callable $handler) : Response
{
//扣除积分
$requestRoute = $request->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;
}
}
}

33
app/model/admin/PointDeductConfig.php

@ -0,0 +1,33 @@
<?php
namespace app\model\admin;
use plugin\piadmin\app\base\BaseModel;
/**
* 套餐模型
*/
class PointDeductConfig extends BaseModel
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'geo_point_deduct_config';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
}

33
app/model/user/UserPackage.php

@ -0,0 +1,33 @@
<?php
namespace app\model\user;
use plugin\piadmin\app\base\BaseModel;
/**
* AI创作指令模型
*/
class UserPackage extends BaseModel
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'geo_user_package';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
}

33
app/model/user/UserPointLog.php

@ -0,0 +1,33 @@
<?php
namespace app\model\user;
use plugin\piadmin\app\base\BaseModel;
/**
* AI创作指令模型
*/
class UserPointLog extends BaseModel
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'geo_user_point_log';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = true;
}

10
app/route/admin.php

@ -4,6 +4,7 @@ use app\controller\admin\ApiController;
use app\controller\admin\OnlineMediasConfigController; use app\controller\admin\OnlineMediasConfigController;
use app\controller\admin\OnlineMediasController; use app\controller\admin\OnlineMediasController;
use app\controller\admin\PackageController; use app\controller\admin\PackageController;
use app\controller\admin\PointDeductConfigController;
use plugin\piadmin\app\middleware\AdminAuthorizationMiddleware; use plugin\piadmin\app\middleware\AdminAuthorizationMiddleware;
use plugin\piadmin\app\middleware\PermissionsMiddleware; use plugin\piadmin\app\middleware\PermissionsMiddleware;
use Webman\Route; use Webman\Route;
@ -52,6 +53,15 @@ Route::group('/service/v1', function () {
Route::get('/pure/index', [PackageController::class, 'pureIndex'])->setParams(['perm' => 'packageIndex']); Route::get('/pure/index', [PackageController::class, 'pureIndex'])->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([ })->middleware([
AdminAuthorizationMiddleware::class, AdminAuthorizationMiddleware::class,
PermissionsMiddleware::class PermissionsMiddleware::class

14
app/route/route.php

@ -16,6 +16,9 @@ use app\controller\user\ExpiosiveReplicaController;
use app\controller\user\GlobalProxyController; use app\controller\user\GlobalProxyController;
use app\controller\user\IndexController; use app\controller\user\IndexController;
use app\controller\user\KnowledgeLibraryController; 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 plugin\piadmin\app\middleware\UserAuthorizationMiddleware;
use Webman\Route; use Webman\Route;
@ -23,6 +26,13 @@ use Webman\Route;
* 用户端定制路由 * 用户端定制路由
*/ */
Route::group('/service/v1', function () { 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 () { Route::group('/databoard', function () {
//数据总量 //数据总量
@ -216,8 +226,8 @@ Route::group('/service/v1', function () {
//详情 //详情
Route::get('/read', [GlobalProxyController::class, 'read'])->setParams(['perm' => ['proxyRead']]); Route::get('/read', [GlobalProxyController::class, 'read'])->setParams(['perm' => ['proxyRead']]);
}); });
})->middleware([ })->middleware([
UserAuthorizationMiddleware::class, UserAuthorizationMiddleware::class,
// AdminAuthorizationMiddleware::class,
// PermissionsMiddleware::class
PointDeductMiddleware::class
]); ]);

114
app/service/admin/PointDeductConfigService.php

@ -0,0 +1,114 @@
<?php
namespace app\service\admin;
use app\dao\admin\PointDeductConfigDao;
use plugin\piadmin\app\base\BaseService;
use plugin\piadmin\app\exception\ApiException;
use plugin\piadmin\app\utils\RequestUtils;
use support\think\Db;
class PointDeductConfigService extends BaseService
{
protected $dao;
public function __construct(PointDeductConfigDao $dao)
{
$this->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];
}
}

43
app/service/user/UserPointLogService.php

@ -0,0 +1,43 @@
<?php
namespace app\service\user;
use app\dao\user\UserPointLogDao;
use plugin\piadmin\app\base\BaseService;
use plugin\piadmin\app\utils\RequestUtils;
class UserPointLogService extends BaseService
{
protected $dao;
public function __construct(UserPointLogDao $dao)
{
$this->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');
}
}

36
app/validate/PointDeductConfigValidate.php

@ -0,0 +1,36 @@
<?php
namespace app\validate;
use plugin\piadmin\app\base\BaseValidate;
class PointDeductConfigValidate extends BaseValidate
{
protected $group = [
'save' => [
'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' => '状态只能为数字',
];
}

6
plugin/piadmin/app/model/User.php

@ -2,6 +2,7 @@
namespace plugin\piadmin\app\model; namespace plugin\piadmin\app\model;
use app\model\user\UserPackage;
use plugin\piadmin\app\base\BaseModel; use plugin\piadmin\app\base\BaseModel;
use think\model\concern\SoftDelete; use think\model\concern\SoftDelete;
@ -18,4 +19,9 @@ class User extends BaseModel
protected $append = []; protected $append = [];
public function package()
{
return $this->hasOne(UserPackage::class, 'uid', 'id');
}
} }

11
plugin/piadmin/app/service/UserService.php

@ -235,6 +235,17 @@ class UserService extends BaseService
'status' => $status 'status' => $status
]; ];
} }
/**
* 用户端获取用户信息
* 包含套餐信息
*/
public function getUserInfoWithPackage()
{
$user = RequestUtils::getUserInfo();
$userinfo = $this->dao->get(['id' => $user['id']], ['*'], ['package']);
return $userinfo;
}
// ============================================================ 私有方法 =============================================== // ============================================================ 私有方法 ===============================================

Loading…
Cancel
Save