Explorar o código

新增账户管理相关接口

lh hai 3 semanas
pai
achega
9511c5d0f1

+ 223 - 0
app/Http/Controllers/Manage/ManageUserController.php

@@ -0,0 +1,223 @@
+<?php
+
+namespace App\Http\Controllers\Manage;
+
+use App\Libs\ApiResponse;
+use App\Libs\Utils;
+use App\Services\Manage\ManageUserService;
+use App\Transformer\Manage\ManageUserTransformer;
+use Illuminate\Http\Request;
+use Illuminate\Routing\Controller as BaseController;
+use Illuminate\Support\Facades\Validator;
+
+class ManageUserController extends BaseController
+{
+    use ApiResponse;
+
+    protected $manageUserService;
+
+    public function __construct(ManageUserService $manageUserService)
+    {
+        $this->manageUserService = $manageUserService;
+    }
+
+    /**
+     * 公司列表(不带分页)
+     *
+     * 权限:仅超管和管理员
+     *
+     * @return mixed
+     */
+    public function companyList()
+    {
+        return $this->success([
+            'list' => $this->manageUserService->getCompanyList(),
+        ]);
+    }
+
+    /**
+     * 角色可选项
+     *
+     * 权限:仅超管和管理员
+     * - superadmin:admin(管理员)、user(组员)
+     * - admin:仅 user(组员)
+     *
+     * @return mixed
+     */
+    public function roleOptions()
+    {
+        return $this->success([
+            'list' => $this->manageUserService->getRoleOptions(),
+        ]);
+    }
+
+    /**
+     * 添加账号
+     *
+     * 权限:仅超管和管理员
+     * - superadmin:选择公司ID(cpid),角色可添加 admin/user
+     * - admin:cpid 默认本公司,角色仅可添加 user
+     *
+     * @param Request $request
+     * @return mixed
+     */
+    public function add(Request $request)
+    {
+        $data = $request->all();
+
+        $validator = Validator::make($data, [
+            'account'    => 'required|string|max:32',
+            'nickname'   => 'required|string|max:100',
+            'pwd'        => 'required|string|min:6|max:32',
+            'is_enabled' => 'required|integer|in:0,1',
+            'role'       => 'required|string|in:admin,user',
+            'cpid'       => 'nullable|integer|min:1',
+        ], [
+            'account.required'    => '请传入账号',
+            'account.max'         => '账号不能超过32个字符',
+            'nickname.required'   => '请传入昵称',
+            'nickname.max'        => '昵称不能超过100个字符',
+            'pwd.required'        => '请传入密码',
+            'pwd.min'             => '密码长度不能少于6位',
+            'pwd.max'             => '密码长度不能超过32位',
+            'is_enabled.required' => '请传入是否启用',
+            'is_enabled.in'       => 'is_enabled仅支持0或1',
+            'role.required'       => '请传入角色',
+            'role.in'             => '角色仅支持admin(公司1管理员)或user(组员)',
+            'cpid.integer'        => 'cpid必须为整数',
+            'cpid.min'            => 'cpid不能小于1',
+        ]);
+
+        if ($validator->fails()) {
+            Utils::throwError('1002:' . $validator->errors()->first());
+        }
+
+        $result = $this->manageUserService->addUser($data);
+
+        return $this->success($result);
+    }
+
+    /**
+     * 用户列表
+     *
+     * 权限:仅超管和管理员
+     * - superadmin:全部用户(id 倒序)
+     * - admin:自己置顶 + 同公司组员(id 倒序)
+     *
+     * @param Request $request
+     * @return mixed
+     */
+    public function list(Request $request)
+    {
+        $data = $request->all();
+
+        $validator = Validator::make($data, [
+            'page'         => 'nullable|integer|min:1',
+            'per_page'     => 'nullable|integer|min:1|max:100',
+            'keyword'      => 'nullable|string|max:100',
+            'nickname'     => 'nullable|string|max:100',
+            'is_enabled'   => 'nullable|integer|in:0,1',
+            'company_name' => 'nullable|string|max:100',
+        ], [
+            'page.integer'         => 'page必须为整数',
+            'page.min'             => 'page不能小于1',
+            'per_page.integer'     => 'per_page必须为整数',
+            'per_page.min'         => 'per_page不能小于1',
+            'per_page.max'         => 'per_page不能超过100',
+            'keyword.max'          => 'keyword不能超过100个字符',
+            'nickname.max'         => 'nickname不能超过100个字符',
+            'is_enabled.in'        => 'is_enabled仅支持0或1',
+            'company_name.max'     => 'company_name不能超过100个字符',
+        ]);
+
+        if ($validator->fails()) {
+            Utils::throwError('1002:' . $validator->errors()->first());
+        }
+
+        $result = $this->manageUserService->getUserList($data);
+
+        return $this->success([
+            'meta' => getMeta($result),
+            'list' => (new ManageUserTransformer())->newEachUser($result),
+        ]);
+    }
+
+    /**
+     * 修改用户
+     *
+     * 权限:仅超管和管理员
+     * - superadmin:任意用户,可改昵称/密码/是否启用/角色
+     * - admin:仅同公司组员,可改昵称/密码/是否启用
+     *
+     * @param Request $request
+     * @return mixed
+     */
+    public function edit(Request $request)
+    {
+        $data = $request->all();
+
+        $validator = Validator::make($data, [
+            'uid'        => 'required|integer|min:1',
+            'nickname'   => 'nullable|string|max:100',
+            'pwd'        => 'nullable|string|min:6|max:32',
+            'is_enabled' => 'nullable|integer|in:0,1',
+            'role'       => 'nullable|string|in:superadmin,admin,user',
+        ], [
+            'uid.required'     => '请传入目标用户uid',
+            'uid.integer'      => 'uid必须为整数',
+            'uid.min'          => 'uid不能小于1',
+            'nickname.max'     => '昵称不能超过100个字符',
+            'pwd.min'          => '密码长度不能少于6位',
+            'pwd.max'          => '密码长度不能超过32位',
+            'is_enabled.in'    => 'is_enabled仅支持0或1',
+            'role.in'          => '角色仅支持superadmin、admin或user',
+        ]);
+
+        if ($validator->fails()) {
+            Utils::throwError('1002:' . $validator->errors()->first());
+        }
+
+        $hasAny = array_key_exists('nickname', $data)
+            || array_key_exists('pwd', $data)
+            || array_key_exists('is_enabled', $data)
+            || array_key_exists('role', $data);
+        if (!$hasAny) {
+            Utils::throwError('1002:请至少传入昵称、密码、是否启用或角色之一');
+        }
+
+        $result = $this->manageUserService->updateUser($data);
+
+        return $this->success($result);
+    }
+
+    /**
+     * 删除用户
+     *
+     * 权限:仅超管和管理员
+     * - superadmin:可删除 admin/user,不允许删除超管
+     * - admin:仅可删除同公司组员
+     *
+     * @param Request $request
+     * @return mixed
+     */
+    public function delete(Request $request)
+    {
+        $data = $request->all();
+
+        $validator = Validator::make($data, [
+            'uid' => 'required|integer|min:1',
+        ], [
+            'uid.required' => '请传入目标用户uid',
+            'uid.integer'  => 'uid必须为整数',
+            'uid.min'      => 'uid不能小于1',
+        ]);
+
+        if ($validator->fails()) {
+            Utils::throwError('1002:' . $validator->errors()->first());
+        }
+
+        $result = $this->manageUserService->deleteUser($data);
+
+        return $this->success(['success' => $result ? 1 : 0]);
+    }
+}

+ 2 - 2
app/Http/Middleware/CheckTokenTrait.php

@@ -13,7 +13,7 @@ trait CheckTokenTrait
     public function checkTokenTrait($token)
     {
         // 获取用户信息
-        $user = DB::table('mp_manage_users')->where('token', $token)->first();
+        $user = DB::table('mp_manage_users')->where('token', $token)->where('is_deleted', 0)->first();
         $uid  = (int)getProp($user, 'id');
         if (!$uid) {
             Utils::throwError(ErrorConst::NOT_LOGIN);
@@ -28,4 +28,4 @@ trait CheckTokenTrait
         $site->cpid     = getProp($user, 'cpid');
         $site->token    = $token;
     }
-}
+}

+ 2 - 2
app/Services/Account/AccountService.php

@@ -25,7 +25,7 @@ class AccountService
         // 密码
         $pwd = md5($passwd . $this->salt);
 
-        $user = DB::table('mp_manage_users')->where('account', $account)->where('pwd', $pwd)->first();
+        $user = DB::table('mp_manage_users')->where('account', $account)->where('pwd', $pwd)->where('is_deleted', 0)->first();
         if (!$user) {
             Utils::throwError('20003:用户不存在');
         }
@@ -55,7 +55,7 @@ class AccountService
     {
         $uid = Site::getUid();
 
-        $user = DB::table('mp_manage_users')->where('id', $uid)->first();
+        $user = DB::table('mp_manage_users')->where('id', $uid)->where('is_deleted', 0)->first();
         if (!$user) {
             Utils::throwError('20003:用户不存在');
         }

+ 0 - 71
app/Services/DeepSeek/DeepSeekService.php

@@ -14329,77 +14329,6 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
         }
     }
 
-    private function splitContent($content) {
-        // 根据章节标题来拆分章节内容,返回包含标题和内容的二维数组
-        $chapters = [];
-        
-        // 匹配章节标题格式:
-        // 1. 标题可能独占一行,也可能在正文末尾或中间
-        // 2. 格式支持:
-        //    - **第一集**
-        //    - 第一集:
-        //    - ###第1章 重生
-        //    - ##第2章 相救
-        //    - 第三章 共处一室
-        // 3. 支持阿拉伯数字(0-9)和中文数字(一二两三四五六七八九十百千万)
-        // 4. 支持章节关键字:章、节、集、幕、场、回、话
-        // 5. "第"字与数字、数字与章节关键字之间可能有空格
-        // 6. 前后可能有#、*、·等修饰符号,后面可能有冒号、空格、标题文字等
-        
-        // 先预处理:将标题前后添加换行符,确保标题能被正确识别和拆分
-        // 匹配模式:可能有前缀符号 + "第" + 空格(可选) + 数字 + 空格(可选) + 章节关键字 + 可能的标题内容
-        $pattern = '/([#*·\-_=\s]*)第\s*([一二两三四五六七八九十百千万0-9]+)\s*([章节集幕场回话])([^第\n]*?)(?=第\s*[一二两三四五六七八九十百千万0-9]+\s*[章节集幕场回话]|$)/u';
-        
-        // 先在每个标题前插入换行符(如果前面不是换行的话)
-        $content = preg_replace('/([^\n])([#*·\-_=\s]*)第\s*([一二两三四五六七八九十百千万0-9]+)\s*([章节集幕场回话])/u', "$1\n\n$2第$3$4", $content);
-        
-        // 现在用修改后的模式匹配(标题应该在行首或接近行首)
-        $pattern = '/^[#*·\s\-_=]*第\s*([一二两三四五六七八九十百千万0-9]+)\s*([章节集幕场回话]).*$/mu';
-        
-        // 用正则找出所有章节标题的位置
-        if (preg_match_all($pattern, $content, $matches, PREG_OFFSET_CAPTURE)) {
-            $titleCount = count($matches[0]);
-            
-            for ($i = 0; $i < $titleCount; $i++) {
-                // 获取当前章节标题
-                $titleLine = trim($matches[0][$i][0]);
-                // 去除标题开头的特殊符号
-                $cleanTitle = preg_replace('/^[#*·\-_=\s]+/u', '', $titleLine);
-                // 去除标题结尾的冒号和特殊符号
-                $cleanTitle = preg_replace('/[#*·::\s]+$/u', '', $cleanTitle);
-                $cleanTitle = trim($cleanTitle);
-                
-                // 获取当前标题的结束位置
-                $titleEndPos = $matches[0][$i][1] + strlen($matches[0][$i][0]);
-                
-                // 获取下一个章节标题的开始位置(如果存在)
-                $nextTitlePos = ($i < $titleCount - 1) ? $matches[0][$i + 1][1] : strlen($content);
-                
-                // 提取章节内容(从标题结束到下一个标题开始)
-                $chapterContent = substr($content, $titleEndPos, $nextTitlePos - $titleEndPos);
-                // 清理内容:去除前后空白和分隔线
-                $chapterContent = trim($chapterContent);
-                $chapterContent = preg_replace('/^[\-\s]+/u', '', $chapterContent);
-                $chapterContent = preg_replace('/[\-\s]+$/u', '', $chapterContent);
-                
-                if (!empty($cleanTitle) && !empty($chapterContent)) {
-                    $chapters[] = [
-                        'title' => $cleanTitle,
-                        'content' => $chapterContent
-                    ];
-                }
-            }
-        } else {
-            // 如果没有匹配到章节,返回整个内容
-            $chapters[] = [
-                'title' => '',
-                'content' => $content
-            ];
-        }
-        
-        return $chapters;
-    }
-
     public function getContentByBid($bid) {
         $chapters = DB::table('chapters as c')->select('c.id', 'c.name', 'cc.content')
         ->leftJoin('chapter_contents as cc', 'c.chapter_content_id', '=', 'cc.id')

+ 374 - 0
app/Services/Manage/ManageUserService.php

@@ -0,0 +1,374 @@
+<?php
+
+namespace App\Services\Manage;
+
+use App\Consts\ErrorConst;
+use App\Facade\Site;
+use App\Libs\Utils;
+use Illuminate\Support\Facades\DB;
+
+class ManageUserService
+{
+    private $salt = 'bXBfYXVkaW8=';
+
+    /**
+     * 公司列表(不带分页)
+     *
+     * 权限:仅超管和管理员
+     *
+     * @return array
+     */
+    public function getCompanyList(): array
+    {
+        $role = (string)Site::getRole();
+        if (!in_array($role, ['superadmin', 'admin'], true)) {
+            Utils::throwError('1005:当前角色无权查看公司列表');
+        }
+
+        $companies = DB::table('mp_company')->orderBy('id')->get(['id', 'company_name']);
+
+        $result = [];
+        foreach ($companies as $company) {
+            $result[] = [
+                'cpid'         => (int)getProp($company, 'id', 0),
+                'company_name' => (string)getProp($company, 'company_name', ''),
+            ];
+        }
+
+        return $result;
+    }
+
+    /**
+     * 角色可选项
+     *
+     * 权限:仅超管和管理员
+     * - superadmin:admin(管理员)、user(组员)
+     * - admin:仅 user(组员)
+     *
+     * @return array
+     */
+    public function getRoleOptions(): array
+    {
+        $role = (string)Site::getRole();
+        if (!in_array($role, ['superadmin', 'admin'], true)) {
+            Utils::throwError('1005:当前角色无权查看角色选项');
+        }
+
+        $options = [
+            ['value' => 'admin', 'name' => '管理员'],
+            ['value' => 'user', 'name' => '组员'],
+        ];
+
+        // 管理员只能添加组员
+        if ($role === 'admin') {
+            $options = array_values(array_filter($options, function ($item) {
+                return $item['value'] === 'user';
+            }));
+        }
+
+        return $options;
+    }
+
+    /**
+     * 添加账号
+     *
+     * 权限:仅超管和管理员
+     * - superadmin:选择公司ID(cpid),角色可添加 admin/user
+     * - admin:cpid 默认本公司,角色仅可添加 user
+     *
+     * @param array $params
+     * @return array
+     */
+    public function addUser(array $params): array
+    {
+        $role = (string)Site::getRole();
+        if (!in_array($role, ['superadmin', 'admin'], true)) {
+            Utils::throwError('1005:当前角色无权添加账号');
+        }
+
+        $account   = trim((string)getProp($params, 'account', ''));
+        $nickname  = trim((string)getProp($params, 'nickname', ''));
+        $pwd       = (string)getProp($params, 'pwd', '');
+        $isEnabled = (int)getProp($params, 'is_enabled', 1);
+        $newRole   = (string)getProp($params, 'role', '');
+
+        if ($account === '') {
+            Utils::throwError('1002:请传入账号');
+        }
+        if ($nickname === '') {
+            Utils::throwError('1002:请传入昵称');
+        }
+        if ($pwd === '') {
+            Utils::throwError('1002:请传入密码');
+        }
+        if (!in_array($newRole, ['admin', 'user'], true)) {
+            Utils::throwError('1002:角色仅支持admin(管理员)或user(组员)');
+        }
+        if ($role === 'admin' && $newRole !== 'user') {
+            Utils::throwError('1005:管理员仅可添加user(组员)角色');
+        }
+
+        if ($role === 'superadmin') {
+            $cpid = (int)getProp($params, 'cpid', 0);
+            if ($cpid < 1) {
+                Utils::throwError('1002:超管添加账号需选择公司');
+            }
+            if (!DB::table('mp_company')->where('id', $cpid)->exists()) {
+                Utils::throwError('1002:所选公司不存在');
+            }
+        } else {
+            // 管理员默认本公司
+            $cpid = (int)Site::getCpid();
+        }
+
+        // 组员数量上限:同一公司 role=user 最多 20 人
+        if ($newRole === 'user') {
+            $groupCount = DB::table('mp_manage_users')
+                ->where('role', 'user')
+                ->where('cpid', $cpid)
+                ->where('is_deleted', 0)
+                ->count();
+            if ($groupCount >= 20) {
+                Utils::throwError('1002:该公司组员数量已达上限(20人)');
+            }
+        }
+
+        if (DB::table('mp_manage_users')->where('account', $account)->where('is_deleted', 0)->exists()) {
+            Utils::throwError('1002:账号已存在');
+        }
+
+        $uid = DB::table('mp_manage_users')->insertGetId([
+            'account'    => $account,
+            'nickname'   => $nickname,
+            'o_pass'     => $pwd,
+            'pwd'        => md5($pwd . $this->salt),
+            'role'       => $newRole,
+            'cpid'       => $cpid,
+            'is_enabled' => $isEnabled,
+            'is_deleted' => 0,
+            'created_at' => date('Y-m-d H:i:s'),
+            'updated_at' => date('Y-m-d H:i:s'),
+        ]);
+
+        return $this->buildUserResult($uid);
+    }
+
+    /**
+     * 构建用户返回信息(添加/修改用户成功后返回)
+     *
+     * @param int $uid
+     * @return array
+     */
+    private function buildUserResult(int $uid): array
+    {
+        $user = DB::table('mp_manage_users')->where('id', $uid)->where('is_deleted', 0)->first();
+        if (!$user) {
+            Utils::throwError(ErrorConst::USER_IS_NOT_EXIST);
+        }
+
+        return [
+            'uid'        => (int)getProp($user, 'id', 0),
+            'account'    => (string)getProp($user, 'account', ''),
+            'nickname'   => (string)getProp($user, 'nickname', ''),
+            'is_enabled' => (int)getProp($user, 'is_enabled', 0),
+            'role'       => (string)getProp($user, 'role', ''),
+            'points'     => (float)getProp($user, 'points', 0),
+            'created_at' => transDate(getProp($user, 'created_at')),
+        ];
+    }
+
+    /**
+     * 用户列表
+     *
+     * 权限范围:
+     * - superadmin:全部用户,按 id 倒序
+     * - admin:自己置顶,同公司组员(role=user 且同 cpid)按 id 倒序
+     * - 其他角色无权查看
+     *
+     * @param array $params
+     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
+     */
+    public function getUserList(array $params)
+    {
+        $role = (string)Site::getRole();
+        if (!in_array($role, ['superadmin', 'admin'], true)) {
+            Utils::throwError('1005:当前角色无权查看用户列表');
+        }
+
+        $perPage = (int)getProp($params, 'per_page', 15);
+        if ($perPage < 1) {
+            $perPage = 15;
+        }
+
+        $query = DB::table('mp_manage_users as u')
+            ->where('u.is_deleted', 0)
+            ->leftJoin('mp_company as c', 'c.id', '=', 'u.cpid')
+            ->select(
+                'u.id',
+                'u.account',
+                'u.nickname',
+                'u.role',
+                'u.is_enabled',
+                'u.points',
+                'u.created_at',
+                'c.company_name'
+            );
+
+        if ($role === 'admin') {
+            $uid  = (int)Site::getUid();
+            $cpid = (int)Site::getCpid();
+            $query->where(function ($q) use ($uid, $cpid) {
+                $q->where('u.id', $uid)
+                    ->orWhere(function ($q2) use ($cpid) {
+                        $q2->where('u.role', 'user')->where('u.cpid', $cpid);
+                    });
+            });
+            // 自己最前,组员按 id 倒序
+            $query->orderByRaw('(u.id = ?) DESC, u.id DESC', [$uid]);
+        } else {
+            // superadmin:全部用户按 id 倒序
+            $query->orderByDesc('u.id');
+        }
+
+        // 关键词筛选:同时模糊匹配账号、昵称(兼容 nickname 参数名)
+        $keyword = trim((string)getProp($params, 'nickname', ''));
+        if ($keyword !== '') {
+            $query->where(function ($q) use ($keyword) {
+                $q->where('u.account', 'like', "%{$keyword}%")
+                    ->orWhere('u.nickname', 'like', "%{$keyword}%");
+            });
+        }
+
+        // 是否启用筛选
+        if (array_key_exists('is_enabled', $params) && $params['is_enabled'] !== '' && $params['is_enabled'] !== null) {
+            $query->where('u.is_enabled', (int)$params['is_enabled']);
+        }
+
+        // 公司名模糊筛选
+        $companyName = trim((string)getProp($params, 'company_name', ''));
+        if ($companyName !== '') {
+            $query->where('c.company_name', 'like', "%{$companyName}%");
+        }
+
+        return $query->paginate($perPage);
+    }
+
+    /**
+     * 修改用户
+     *
+     * 权限范围:
+     * - superadmin:可修改任意用户,支持昵称/密码/是否启用/角色
+     * - admin:仅可修改同公司组员(role=user 且同 cpid),支持昵称/密码/是否启用
+     *
+     * @param array $params
+     * @return array
+     */
+    public function updateUser(array $params): array
+    {
+        $role = (string)Site::getRole();
+        if (!in_array($role, ['superadmin', 'admin'], true)) {
+            Utils::throwError('1005:当前角色无权修改用户');
+        }
+
+        $uid = (int)getProp($params, 'uid', 0);
+        if ($uid < 1) {
+            Utils::throwError('1002:请传入目标用户uid');
+        }
+
+        $target = DB::table('mp_manage_users')->where('id', $uid)->where('is_deleted', 0)->first();
+        if (!$target) {
+            Utils::throwError(ErrorConst::USER_IS_NOT_EXIST);
+        }
+
+        $update = [];
+
+        $nickname = getProp($params, 'nickname');
+        if ($nickname !== null && $nickname !== '') {
+            $update['nickname'] = (string)$nickname;
+        }
+
+        $pwd = getProp($params, 'pwd');
+        if ($pwd !== null && $pwd !== '') {
+            $update['pwd']    = md5((string)$pwd . $this->salt);
+            $update['o_pass'] = (string)$pwd;
+        }
+
+        if (array_key_exists('is_enabled', $params) && $params['is_enabled'] !== '' && $params['is_enabled'] !== null) {
+            $update['is_enabled'] = (int)$params['is_enabled'];
+        }
+
+        $newRole = getProp($params, 'role');
+        if ($newRole !== null && $newRole !== '') {
+            if ($role !== 'superadmin') {
+                Utils::throwError('1005:仅超管可调整用户角色');
+            }
+            if (!in_array((string)$newRole, ['superadmin', 'admin', 'user'], true)) {
+                Utils::throwError('1002:角色参数不合法');
+            }
+            $update['role'] = (string)$newRole;
+        }
+
+        if (empty($update)) {
+            Utils::throwError('1002:未传入需要修改的字段');
+        }
+
+        // 管理员仅可管理自己的组员(同公司 role=user)
+        if ($role === 'admin') {
+            $cpid = (int)Site::getCpid();
+            if ((string)$target->role !== 'user' || (int)$target->cpid !== $cpid) {
+                Utils::throwError('1005:仅可修改自己公司的组员用户');
+            }
+        }
+
+        $update['updated_at'] = date('Y-m-d H:i:s');
+        DB::table('mp_manage_users')->where('id', $uid)->update($update);
+
+        return $this->buildUserResult($uid);
+    }
+
+    /**
+     * 删除用户
+     *
+     * 权限范围:
+     * - superadmin:可删除 admin/user,不允许删除超管账号
+     * - admin:仅可删除同公司组员(role=user 且同 cpid)
+     *
+     * @param array $params
+     * @return mixed
+     */
+    public function deleteUser(array $params)
+    {
+        $role = (string)Site::getRole();
+        if (!in_array($role, ['superadmin', 'admin'], true)) {
+            Utils::throwError('1005:当前角色无权删除用户');
+        }
+
+        $uid = (int)getProp($params, 'uid', 0);
+        if ($uid < 1) {
+            Utils::throwError('1002:请传入目标用户uid');
+        }
+
+        $target = DB::table('mp_manage_users')->where('id', $uid)->where('is_deleted', 0)->first();
+        if (!$target) {
+            Utils::throwError(ErrorConst::USER_IS_NOT_EXIST);
+        }
+
+        if ($role === 'superadmin') {
+            if ((string)$target->role === 'superadmin') {
+                Utils::throwError('1005:不允许删除超管账号');
+            }
+        } else {
+            // 管理员仅可删除自己的组员(同公司 role=user)
+            $cpid = (int)Site::getCpid();
+            if ((string)$target->role !== 'user' || (int)$target->cpid !== $cpid) {
+                Utils::throwError('1005:仅可删除自己公司的组员用户');
+            }
+        }
+
+        // 软删除:标记 is_deleted=1
+        return DB::table('mp_manage_users')->where('id', $uid)->update([
+            'is_deleted' => 1,
+            'updated_at' => date('Y-m-d H:i:s'),
+        ]);
+    }
+}

+ 7 - 7
app/Services/PointsService.php

@@ -525,7 +525,7 @@ class PointsService
 
         // points_balance 始终为当前登录用户自己的余额,与查询目标无关
         $loginUid = (int)Site::getUid();
-        $user = $loginUid ? DB::table('mp_manage_users')->where('id', $loginUid)->first() : null;
+        $user = $loginUid ? DB::table('mp_manage_users')->where('id', $loginUid)->where('is_deleted', 0)->first() : null;
         $summary = [
             'points_balance' => (int)getProp($user, 'points', 0),
             // 消耗积分(不排除测试数据,全量口径)
@@ -836,12 +836,12 @@ class PointsService
             Utils::throwError(ErrorConst::NOT_LOGIN);
         }
         $fromRole = (string)Site::getRole();
-        $fromUser = DB::table('mp_manage_users')->where('id', $fromUid)->first();
+        $fromUser = DB::table('mp_manage_users')->where('id', $fromUid)->where('is_deleted', 0)->first();
         if (!$fromUser) {
             Utils::throwError(ErrorConst::USER_IS_NOT_EXIST);
         }
 
-        $targetUser = DB::table('mp_manage_users')->where('id', $targetUid)->first();
+        $targetUser = DB::table('mp_manage_users')->where('id', $targetUid)->where('is_deleted', 0)->first();
         if (!$targetUser) {
             Utils::throwError('20003:目标用户不存在');
         }
@@ -1061,7 +1061,7 @@ class PointsService
     {
         $role = (string)Site::getRole();
 
-        $query = DB::table('mp_manage_users')->where('is_enabled', 1);
+        $query = DB::table('mp_manage_users')->where('is_enabled', 1)->where('is_deleted', 0);
         if ($role === 'superadmin') {
             $query->where('role', 'admin');
         } elseif ($role === 'admin') {
@@ -1102,7 +1102,7 @@ class PointsService
             Utils::throwError(ErrorConst::NOT_LOGIN);
         }
 
-        $user = DB::table('mp_manage_users')->where('id', $uid)->first();
+        $user = DB::table('mp_manage_users')->where('id', $uid)->where('is_deleted', 0)->first();
         if (!$user) {
             Utils::throwError(ErrorConst::USER_IS_NOT_EXIST);
         }
@@ -1412,7 +1412,7 @@ class PointsService
         }
 
         try {
-            $user = DB::table('mp_manage_users')->where('id', $uid)->first();
+            $user = DB::table('mp_manage_users')->where('id', $uid)->where('is_deleted', 0)->first();
             $cpid = (int)getProp($user, 'cpid', 0);
             $pointsBalance = (float)getProp($user, 'points', 0);
 
@@ -1532,7 +1532,7 @@ class PointsService
         try {
             DB::beginTransaction();
 
-            $user = DB::table('mp_manage_users')->where('id', $uid)->first();
+            $user = DB::table('mp_manage_users')->where('id', $uid)->where('is_deleted', 0)->first();
             if (!$user) {
                 DB::rollBack();
                 dLog('points')->error('扣费失败:用户不存在', ['task_id' => $taskId, 'uid' => $uid]);

+ 1 - 0
app/Services/PointsStatsService.php

@@ -273,6 +273,7 @@ class PointsStatsService
                 $q->where('id', $scope['uid']);
             })
             ->where('is_enabled', 1)
+            ->where('is_deleted', 0)
             ->select('id', 'nickname', 'account')
             ->orderBy('id')
             ->get()

+ 43 - 0
app/Transformer/Manage/ManageUserTransformer.php

@@ -0,0 +1,43 @@
+<?php
+
+namespace App\Transformer\Manage;
+
+class ManageUserTransformer
+{
+    const ROLE_LABELS = [
+        'superadmin' => '超级管理员',
+        'admin'      => '公司管理员',
+        'user'       => '组员',
+    ];
+
+    /**
+     * 用户列表单项
+     *
+     * @param $list
+     * @return array
+     */
+    public function newEachUser($list): array
+    {
+        $result = [];
+        if (empty($list)) {
+            return $result;
+        }
+
+        foreach ($list as $item) {
+            $role = (string)getProp($item, 'role', '');
+            $result[] = [
+                'uid'           => (int)getProp($item, 'id', 0),
+                'account'      => (string)getProp($item, 'account', ''),
+                'nickname'     => (string)getProp($item, 'nickname', ''),
+                'role'         => $role,
+                'role_info'    => self::ROLE_LABELS[$role] ?? $role,
+                'is_enabled'   => (int)getProp($item, 'is_enabled', 0),
+                'company_name' => (string)getProp($item, 'company_name', ''),
+                'points'       => (float)getProp($item, 'points', 0),
+                'created_at'   => transDate(getProp($item, 'created_at')),
+            ];
+        }
+
+        return $result;
+    }
+}

+ 13 - 0
routes/api.php

@@ -11,6 +11,7 @@ use App\Http\Controllers\Canvas\CanvasController;
 use App\Http\Controllers\PromptTemplate\PromptTemplateController;
 use App\Http\Controllers\Points\PointsController;
 use App\Http\Controllers\Points\PointsStatsController;
+use App\Http\Controllers\Manage\ManageUserController;
 use App\Http\Controllers\TaskCenter\TaskCenterController;
 use Illuminate\Support\Facades\Route;
 
@@ -101,6 +102,18 @@ Route::group(['middleware' => ['bindToken', 'bindExportToken', 'checkLogin']], f
         Route::get('grantUsers', [PointsController::class, 'grantUsers']);   // 可发放积分用户列表
     });
 
+    Route::group(['prefix' => 'account'], function () {
+        Route::get('list', [ManageUserController::class, 'list']);   // 用户列表(超管全部 / 管理员自己+组员)
+        Route::post('add', [ManageUserController::class, 'add']);    // 添加账号(超管选公司/角色,管理员默认本公司且仅组员)
+        Route::post('edit', [ManageUserController::class, 'edit']);  // 修改用户(昵称/密码/是否启用/角色)
+        Route::get('delete', [ManageUserController::class, 'delete']); // 删除用户(超管删 admin/user,管理员删自己的组员)
+        Route::get('roleOptions', [ManageUserController::class, 'roleOptions']); // 角色可选项(超管 admin/user,管理员仅 user)
+    });
+
+    Route::group(['prefix' => 'company'], function () {
+        Route::get('list', [ManageUserController::class, 'companyList']); // 公司列表(不带分页)
+    });
+
 
     Route::group(['prefix' => 'deepseek'], function () {
         Route::post('chatWithReasoner', [DeepSeekController::class, 'chatWithReasoner']);