Browse Source
feat(admin): 新增点数抵扣配置功能
feat(admin): 新增点数抵扣配置功能
- 添加点数抵扣配置控制器、模型、验证器及服务层 - 配置点数抵扣相关路由并设置权限控制 - 实现点数抵扣中间件,支持请求前扣除点数及失败后返还逻辑 - 新增用户套餐与点数日志关联模型及数据访问对象 - 在用户端路由中增加用户信息及点数记录查询接口 - 扩展用户服务,支持获取包含套餐信息的用户详情master
17 changed files with 611 additions and 2 deletions
-
59app/controller/admin/PointDeductConfigController.php
-
13app/controller/user/UserController.php
-
18app/controller/user/UserPointLogController.php
-
15app/dao/admin/PointDeductConfigDao.php
-
15app/dao/user/UserPackageDao.php
-
15app/dao/user/UserPointLogDao.php
-
145app/middleware/PointDeductMiddleware.php
-
33app/model/admin/PointDeductConfig.php
-
33app/model/user/UserPackage.php
-
33app/model/user/UserPointLog.php
-
10app/route/admin.php
-
14app/route/route.php
-
114app/service/admin/PointDeductConfigService.php
-
43app/service/user/UserPointLogService.php
-
36app/validate/PointDeductConfigValidate.php
-
6plugin/piadmin/app/model/User.php
-
11plugin/piadmin/app/service/UserService.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)); |
|||
} |
|||
|
|||
} |
|||
@ -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()); |
|||
} |
|||
|
|||
} |
|||
@ -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)); |
|||
} |
|||
|
|||
} |
|||
@ -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; |
|||
} |
|||
|
|||
} |
|||
@ -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; |
|||
} |
|||
|
|||
} |
|||
@ -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; |
|||
} |
|||
|
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
|
|||
} |
|||
@ -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; |
|||
|
|||
} |
|||
@ -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; |
|||
|
|||
} |
|||
@ -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]; |
|||
} |
|||
|
|||
} |
|||
@ -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'); |
|||
} |
|||
|
|||
} |
|||
@ -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' => '状态只能为数字', |
|||
]; |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue