|
|
@@ -532,8 +532,14 @@ class PointsService
|
|
|
->sum('points_consumed'),
|
|
|
'total_points_refunded' => (float)MpUserPointsDetail::where('uid', $uid)
|
|
|
->where('points_consumed', '<', 0)
|
|
|
+ ->whereNotIn('type', [MpUserPointsDetail::TYPE_SYSTEM, MpUserPointsDetail::TYPE_COMPANY])
|
|
|
->where($excludeTestRecords)
|
|
|
->sum('points_consumed'),
|
|
|
+ 'total_points_granted' => (float)abs(MpUserPointsDetail::where('uid', $uid)
|
|
|
+ ->whereIn('type', [MpUserPointsDetail::TYPE_SYSTEM, MpUserPointsDetail::TYPE_COMPANY])
|
|
|
+ ->where('points_consumed', '<', 0)
|
|
|
+ ->where($excludeTestRecords)
|
|
|
+ ->sum('points_consumed')),
|
|
|
'total_tokens_consumed' => (int)MpUserPointsDetail::where('uid', $uid)
|
|
|
->where($excludeTestRecords)
|
|
|
->sum('tokens_consumed'),
|
|
|
@@ -549,6 +555,194 @@ class PointsService
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * 发放积分(公共方法)
|
|
|
+ *
|
|
|
+ * 权限规则:
|
|
|
+ * - superadmin(平台):仅可给 role=admin(组织)发放积分,明细 type=system
|
|
|
+ * - admin(组织):仅可给同 cpid 的 role=user(组员)发放积分,明细 type=company
|
|
|
+ * - 其他角色不允许发放
|
|
|
+ *
|
|
|
+ * 发放成功后写入 mp_user_points_details(points_consumed 记负值,表示积分流入),
|
|
|
+ * 并原子累加目标用户余额。
|
|
|
+ *
|
|
|
+ * @param array $params uid(目标用户ID)/ points(发放积分数,正数)/ remark(可选文案)
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public function grantPoints(array $params): array
|
|
|
+ {
|
|
|
+ $fromUid = (int)Site::getUid();
|
|
|
+ $fromRole = (string)Site::getRole();
|
|
|
+ if ($fromUid <= 0) {
|
|
|
+ Utils::throwError(ErrorConst::NOT_LOGIN);
|
|
|
+ }
|
|
|
+ $fromUser = DB::table('mp_manage_users')->where('id', $fromUid)->first();
|
|
|
+ if (!$fromUser) {
|
|
|
+ Utils::throwError(ErrorConst::USER_IS_NOT_EXIST);
|
|
|
+ }
|
|
|
+
|
|
|
+ $targetUid = (int)getProp($params, 'uid', 0);
|
|
|
+ $points = (float)getProp($params, 'points', 0);
|
|
|
+ $remark = trim((string)getProp($params, 'remark', ''));
|
|
|
+
|
|
|
+ if ($targetUid <= 0) {
|
|
|
+ Utils::throwError('1002:请传入目标用户uid');
|
|
|
+ }
|
|
|
+ if ($points <= 0) {
|
|
|
+ Utils::throwError('1002:发放积分数必须大于0');
|
|
|
+ }
|
|
|
+ if (floor($points) != $points) {
|
|
|
+ Utils::throwError('1002:发放积分数必须为整数');
|
|
|
+ }
|
|
|
+
|
|
|
+ $targetUser = DB::table('mp_manage_users')->where('id', $targetUid)->first();
|
|
|
+ if (!$targetUser) {
|
|
|
+ Utils::throwError('20003:目标用户不存在');
|
|
|
+ }
|
|
|
+ if ((int)getProp($targetUser, 'is_enabled', 1) === 0) {
|
|
|
+ Utils::throwError('20003:目标用户已被禁用');
|
|
|
+ }
|
|
|
+
|
|
|
+ $targetRole = (string)getProp($targetUser, 'role', '');
|
|
|
+ $targetCpid = (int)getProp($targetUser, 'cpid', 0);
|
|
|
+
|
|
|
+ // 角色与发放对象校验
|
|
|
+ if ($fromRole === 'superadmin') {
|
|
|
+ if ($targetRole !== 'admin') {
|
|
|
+ Utils::throwError('1005:平台仅可给组织发放积分');
|
|
|
+ }
|
|
|
+ $type = MpUserPointsDetail::TYPE_SYSTEM;
|
|
|
+ $defaultRemark = '平台发放积分';
|
|
|
+ } elseif ($fromRole === 'admin') {
|
|
|
+ if ($targetRole !== 'user') {
|
|
|
+ Utils::throwError('1005:组织仅可给同公司的组员发放积分');
|
|
|
+ }
|
|
|
+ if ($targetCpid !== (int)Site::getCpid()) {
|
|
|
+ Utils::throwError('1005:只能给同一公司的组员发放积分');
|
|
|
+ }
|
|
|
+ $type = MpUserPointsDetail::TYPE_COMPANY;
|
|
|
+ $defaultRemark = '组织发放积分';
|
|
|
+ } else {
|
|
|
+ Utils::throwError('1005:当前角色无权发放积分');
|
|
|
+ }
|
|
|
+
|
|
|
+ $finalRemark = $remark !== '' ? $defaultRemark . ':' . $remark : $defaultRemark;
|
|
|
+ $chargeInfo = [
|
|
|
+ 'from_uid' => $fromUid,
|
|
|
+ 'from_role' => $fromRole,
|
|
|
+ 'target_role' => $targetRole,
|
|
|
+ 'target_cpid' => $targetCpid,
|
|
|
+ 'grant_type' => $type,
|
|
|
+ ];
|
|
|
+
|
|
|
+ try {
|
|
|
+ DB::beginTransaction();
|
|
|
+
|
|
|
+ $pointsBefore = (float)getProp($targetUser, 'points', 0);
|
|
|
+ $pointsAfter = $pointsBefore + $points;
|
|
|
+
|
|
|
+ // 原子累加积分余额,避免并发覆盖
|
|
|
+ DB::table('mp_manage_users')->where('id', $targetUid)->update([
|
|
|
+ 'points' => DB::raw('points + ' . (float)$points),
|
|
|
+ 'updated_at' => date('Y-m-d H:i:s'),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 发放明细:points_consumed 记负值,表示积分流入
|
|
|
+ DB::table('mp_user_points_details')->insert([
|
|
|
+ 'uid' => $targetUid,
|
|
|
+ 'cpid' => $targetCpid,
|
|
|
+ 'task_id' => null,
|
|
|
+ 'type' => $type,
|
|
|
+ 'api_type' => 'grant',
|
|
|
+ 'charge_info' => json_encode($chargeInfo, JSON_UNESCAPED_UNICODE),
|
|
|
+ 'points_before' => $pointsBefore,
|
|
|
+ 'points_consumed' => -$points,
|
|
|
+ 'points_after' => $pointsAfter,
|
|
|
+ 'tokens_consumed' => 0,
|
|
|
+ 'remark' => $finalRemark,
|
|
|
+ 'created_at' => date('Y-m-d H:i:s'),
|
|
|
+ 'updated_at' => date('Y-m-d H:i:s'),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ DB::commit();
|
|
|
+
|
|
|
+ dLog('points')->info('积分发放成功', [
|
|
|
+ 'from_uid' => $fromUid,
|
|
|
+ 'from_role' => $fromRole,
|
|
|
+ 'target_uid' => $targetUid,
|
|
|
+ 'points' => $points,
|
|
|
+ 'type' => $type,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'granted' => true,
|
|
|
+ 'type' => $type,
|
|
|
+ 'uid' => $targetUid,
|
|
|
+ 'points' => $points,
|
|
|
+ 'points_before' => $pointsBefore,
|
|
|
+ 'points_after' => $pointsAfter,
|
|
|
+ 'remark' => $finalRemark,
|
|
|
+ ];
|
|
|
+
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ DB::rollBack();
|
|
|
+
|
|
|
+ dLog('points')->error('积分发放失败: ' . $e->getMessage(), [
|
|
|
+ 'from_uid' => $fromUid,
|
|
|
+ 'target_uid' => $targetUid,
|
|
|
+ 'points' => $points,
|
|
|
+ ]);
|
|
|
+ logDB('points', 'error', '积分发放失败', [
|
|
|
+ 'from_uid' => $fromUid,
|
|
|
+ 'target_uid' => $targetUid,
|
|
|
+ 'points' => $points,
|
|
|
+ 'error' => $e->getMessage(),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ Utils::throwError('1006:积分发放失败,请稍后重试');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取可发放积分的用户列表(公共方法)
|
|
|
+ *
|
|
|
+ * 同一接口按当前角色返回不同列表:
|
|
|
+ * - superadmin(平台):所有组织(role=admin)
|
|
|
+ * - admin(组织):同 cpid 的组员(role=user)
|
|
|
+ * - 其他角色不允许查看
|
|
|
+ *
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public function getGrantUserList(): array
|
|
|
+ {
|
|
|
+ $role = (string)Site::getRole();
|
|
|
+
|
|
|
+ $query = DB::table('mp_manage_users')->where('is_enabled', 1);
|
|
|
+ if ($role === 'superadmin') {
|
|
|
+ $query->where('role', 'admin');
|
|
|
+ } elseif ($role === 'admin') {
|
|
|
+ $query->where('role', 'user')->where('cpid', (int)Site::getCpid());
|
|
|
+ } else {
|
|
|
+ Utils::throwError('1005:当前角色无权查看发放列表');
|
|
|
+ }
|
|
|
+
|
|
|
+ $users = $query->orderBy('cpid', 'asc')->orderBy('id', 'asc')
|
|
|
+ ->get(['id', 'account', 'nickname', 'role', 'cpid', 'points', 'is_enabled'])
|
|
|
+ ->map(function ($user) {
|
|
|
+ return [
|
|
|
+ 'id' => (int)getProp($user, 'id', 0),
|
|
|
+ 'account' => (string)getProp($user, 'account', ''),
|
|
|
+ 'nickname' => (string)getProp($user, 'nickname', ''),
|
|
|
+ // 'role' => (string)getProp($user, 'role', ''),
|
|
|
+ // 'cpid' => (int)getProp($user, 'cpid', 0),
|
|
|
+ 'points' => (float)getProp($user, 'points', 0),
|
|
|
+ // 'is_enabled' => (int)getProp($user, 'is_enabled', 1),
|
|
|
+ ];
|
|
|
+ })->all();
|
|
|
+
|
|
|
+ return $users;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* 获取用户当前积分余额
|
|
|
*
|
|
|
* @param int $uid 用户ID,缺省取当前登录用户
|