This commit is contained in:
wong
2025-04-25 16:31:10 +08:00
9 changed files with 161 additions and 28 deletions

View File

@@ -108,10 +108,7 @@ class AccountController extends BaseController
if (empty($password)) { if (empty($password)) {
return errorJson('密码不能为空'); return errorJson('密码不能为空');
} }
$passwordValidation = validateString($password, 'password');
if (!$passwordValidation['status']) {
return errorJson($passwordValidation['message']);
}
if (empty($realName)) { if (empty($realName)) {
return errorJson('真实姓名不能为空'); return errorJson('真实姓名不能为空');
} }

View File

@@ -35,7 +35,7 @@ Route::group('', function () {
// 公司路由 // 公司路由
Route::group('company', function () { Route::group('company', function () {
Route::post('create', 'app\superadmin\controller\company\CreateCompanyController@index'); Route::post('add', 'app\superadmin\controller\company\CreateCompanyController@index');
Route::post('update', 'app\superadmin\controller\company\UpdateCompanyController@index'); Route::post('update', 'app\superadmin\controller\company\UpdateCompanyController@index');
Route::post('delete', 'app\superadmin\controller\company\DeleteCompanyController@index'); Route::post('delete', 'app\superadmin\controller\company\DeleteCompanyController@index');
Route::get('list', 'app\superadmin\controller\company\GetCompanyListController@index'); Route::get('list', 'app\superadmin\controller\company\GetCompanyListController@index');

View File

@@ -17,13 +17,39 @@ use think\Validate;
*/ */
class CreateCompanyController extends BaseController class CreateCompanyController extends BaseController
{ {
/**
* S2 创建用户。
*
* @param array $params
* @return mixed|null
* @throws \Exception
*/
protected function s2CreateUser(array $params): ?array
{
$params = ArrHelper::getValue('account=userName,password,username=realName,username=nickname,companyId=departmentId', $params);
// 创建账号
$response = CurlHandle::getInstant()
->setBaseUrl(Env::get('rpc.API_BASE_URL'))
->setMethod('post')
->send('/v1/api/account/create', $params);
$result = json_decode($response, true);
if ($result['code'] != 200) {
throw new \Exception($result['msg'], 210 . $result['code']);
}
return $result['data'] ?: null;
}
/** /**
* S2 创建部门并返回id * S2 创建部门并返回id
* *
* @param array $params * @param array $params
* @return array * @return array
*/ */
protected function s2CreateDepartment(array $params): ?array protected function s2CreateDepartmentAndUser(array $params): ?array
{ {
$params = ArrHelper::getValue('name=departmentName,memo=departmentMemo,account=accountName,password=accountPassword,username=accountRealName,username=accountNickname,accountMemo', $params); $params = ArrHelper::getValue('name=departmentName,memo=departmentMemo,account=accountName,password=accountPassword,username=accountRealName,username=accountNickname,accountMemo', $params);
@@ -88,7 +114,7 @@ class CreateCompanyController extends BaseController
*/ */
protected function creatS2About(array $params): array protected function creatS2About(array $params): array
{ {
$department = $this->s2CreateDepartment($params); $department = $this->s2CreateDepartmentAndUser($params);
if (!$department || !isset($department['id']) || !isset($department['departmentId'])) { if (!$department || !isset($department['id']) || !isset($department['departmentId'])) {
throw new \Exception('S2返参异常', 210402); throw new \Exception('S2返参异常', 210402);
@@ -117,6 +143,26 @@ class CreateCompanyController extends BaseController
} }
} }
/**
* 创建功能账号,不可登录,也非管理员,用户也不可见.
*
* @param array $params
* @return void
* @throws \Exception
*/
protected function createFuncUsers(array $params): void
{
$seedCols = [
['account' => $params['account'] . '_offline', 'username' => '处理离线专用', 'status' => 0, 'isAdmin' => 0, 'typeId' => -1],
['account' => $params['account'] . '_delete' , 'username' => '处理删除专用', 'status' => 0, 'isAdmin' => 0, 'typeId' => -1],
];
foreach ($seedCols as $seeds) {
$this->s2CreateUser (array_merge($params, ArrHelper::getValue('account,username', $seeds)));
$this->ckbCreateUser(array_merge($params, $seeds));
}
}
/** /**
* 存客宝创建账号 * 存客宝创建账号
* *
@@ -126,19 +172,14 @@ class CreateCompanyController extends BaseController
*/ */
protected function ckbCreateUser(array $params): void protected function ckbCreateUser(array $params): void
{ {
$params = ArrHelper::getValue( $params = ArrHelper::getValue('username,account,password,companyId,s2_accountId,status,phone,isAdmin,typeId', $params);
'username,account,password,companyId,s2_accountId,status,phone',
$params
);
$result = UsersModel::create(array_merge($params, [ $params = array_merge($params, [
'passwordLocal' => localEncrypt($params['password']), 'passwordLocal' => localEncrypt($params['password']),
'passwordMd5' => md5($params['password']), 'passwordMd5' => md5($params['password']),
'isAdmin' => 1, // 主要账号默认1 ]);
'typeId' => 1, // 类型:运营后台/操盘手传1、 门店传2
]));
if (!$result) { if (!UsersModel::create($params)) {
throw new \Exception('创建用户记录失败', 402); throw new \Exception('创建用户记录失败', 402);
} }
} }
@@ -154,7 +195,10 @@ class CreateCompanyController extends BaseController
$this->ckbCreateCompany($params); $this->ckbCreateCompany($params);
// 2. 存客宝创建操盘手总账号 // 2. 存客宝创建操盘手总账号
$this->ckbCreateUser($params); $this->ckbCreateUser(array_merge($params, [
'isAdmin' => 1, // 主要账号默认1
'typeId' => 1, // 类型:运营后台/操盘手传1、 门店传2
]));
} }
/** /**
@@ -199,9 +243,13 @@ class CreateCompanyController extends BaseController
$params = $this->dataValidate($params)->creatS2About($params); $params = $this->dataValidate($params)->creatS2About($params);
Db::startTrans(); Db::startTrans();
$this->checkCompanyNameOrAccountOrPhoneExists(ArrHelper::getValue('name,account,phone', $params)); $this->checkCompanyNameOrAccountOrPhoneExists(ArrHelper::getValue('name,account,phone', $params));
$this->createCkbAbout($params); $this->createCkbAbout($params);
// 创建功能账号,不可登录,也非管理员,用户也不可见
$this->createFuncUsers($params);
Db::commit(); Db::commit();
return ResponseHelper::success(); return ResponseHelper::success();
} catch (\Exception $e) { } catch (\Exception $e) {

View File

@@ -22,7 +22,7 @@ class GetCompanyDetailForUpdateController extends BaseController
{ {
return DeviceModel::alias('d') return DeviceModel::alias('d')
->field([ ->field([
'd.id', 'd.memo', 'd.id', 'd.memo', 'd.model', 'd.brand', 'd.phone', 'd.imei', 'd.createTime', 'd.alive',
]) ])
->where('companyId', $companyId) ->where('companyId', $companyId)
->select() ->select()

View File

@@ -23,6 +23,12 @@ declare module 'react' {
interface Device { interface Device {
id: number id: number
memo: string memo: string
imei: string
phone: string
model: string
brand: string
alive: number
createTime: number
} }
interface Project { interface Project {
@@ -264,14 +270,40 @@ export default function EditProjectPage({ params }: { params: { id: string } })
<div className="space-y-2"> <div className="space-y-2">
<Label></Label> <Label></Label>
<div className="space-y-3"> <div className="space-y-3">
{project && project.devices && project.devices.length > 0 && project.devices.map((device) => ( {project && project.devices && project.devices.length > 0 && (
<div key={device.id} className="flex items-center gap-2"> <div className="border rounded-md">
<Input <table className="w-full table-fixed">
value={device.memo} <thead className="bg-muted" style={{ fontFamily: "'Microsoft YaHei', sans-serif" }}>
readOnly <tr>
/> <th className="text-left p-2 w-[12%]"></th>
<th className="text-left p-2 w-[22%]">IMEI</th>
<th className="text-left p-2 w-[15%]"></th>
<th className="text-left p-2 w-[12%]"></th>
<th className="text-left p-2 w-[10%]"></th>
<th className="text-left p-2 w-[8%]"></th>
<th className="text-left p-2 w-[13%]"></th>
</tr>
</thead>
<tbody className="text-sm" style={{ fontFamily: "'Microsoft YaHei', sans-serif" }}>
{project.devices.map((device) => (
<tr key={device.id} className="border-t">
<td className="p-2 truncate" title={device.memo}>{device.memo}</td>
<td className="p-2" title={device.imei || '-'}>{device.imei || '-'}</td>
<td className="p-2 truncate" title={device.phone || '-'}>{device.phone || '-'}</td>
<td className="p-2 truncate" title={device.model || '-'}>{device.model || '-'}</td>
<td className="p-2 truncate" title={device.brand || '-'}>{device.brand || '-'}</td>
<td className="p-2">
<span className={`inline-block px-2 py-1 text-xs rounded-full ${device.alive === 1 ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}`}>
{device.alive === 1 ? '在线' : '离线'}
</span>
</td>
<td className="p-2 truncate" title={device.createTime || '-'}>{device.createTime || '-'}</td>
</tr>
))}
</tbody>
</table>
</div> </div>
))} )}
<Button <Button
type="button" type="button"
variant="outline" variant="outline"

View File

@@ -342,7 +342,13 @@ export default function ProjectDetailPage({ params }: ProjectDetailPageProps) {
{user.status === 1 ? "启用" : "禁用"} {user.status === 1 ? "启用" : "禁用"}
</Badge> </Badge>
</TableCell> </TableCell>
<TableCell>{user.typeId === 1 ? "操盘手" : "门店顾问"}</TableCell> <TableCell>
{user.typeId === -1
? "系统账号"
: user.typeId === 1
? "操盘手"
: "门店顾问"}
</TableCell>
<TableCell className="text-right">{user.createTime}</TableCell> <TableCell className="text-right">{user.createTime}</TableCell>
</TableRow> </TableRow>
))} ))}

View File

@@ -329,7 +329,13 @@ export default function ProjectDetail({ projectId, onEdit }: ProjectDetailProps)
{user.status === 1 ? "启用" : "禁用"} {user.status === 1 ? "启用" : "禁用"}
</Badge> </Badge>
</TableCell> </TableCell>
<TableCell>{user.typeId === 1 ? "操盘手" : "门店顾问"}</TableCell> <TableCell>
{user.typeId === -1
? "系统账号"
: user.typeId === 1
? "操盘手"
: "门店顾问"}
</TableCell>
<TableCell className="text-right">{user.createTime}</TableCell> <TableCell className="text-right">{user.createTime}</TableCell>
</TableRow> </TableRow>
))} ))}

31
SuperAdmin/middleware.ts Normal file
View File

@@ -0,0 +1,31 @@
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// 获取响应对象
const response = NextResponse.next()
// 设置CORS头
response.headers.set('Access-Control-Allow-Credentials', 'true')
response.headers.set('Access-Control-Allow-Origin', '*') // 在生产环境中应该设置为特定域名
response.headers.set('Access-Control-Allow-Methods', 'GET,DELETE,PATCH,POST,PUT,OPTIONS')
response.headers.set('Access-Control-Allow-Headers', 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, Authorization')
// 处理预检请求
if (request.method === 'OPTIONS') {
return new NextResponse(null, { status: 200, headers: response.headers })
}
return response
}
// 配置中间件应用的路径
export const config = {
matcher: [
// 匹配所有API路由
'/api/:path*',
// 匹配需要跨域的特定外部API请求
'/company/:path*',
'/v1/api/:path*',
],
}

View File

@@ -27,6 +27,19 @@ const nextConfig = {
parallelServerBuildTraces: true, parallelServerBuildTraces: true,
parallelServerCompiles: true, parallelServerCompiles: true,
}, },
async headers() {
return [
{
source: '/api/:path*',
headers: [
{ key: 'Access-Control-Allow-Credentials', value: 'true' },
{ key: 'Access-Control-Allow-Origin', value: '*' },
{ key: 'Access-Control-Allow-Methods', value: 'GET,DELETE,PATCH,POST,PUT,OPTIONS' },
{ key: 'Access-Control-Allow-Headers', value: 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, Authorization' },
],
},
]
},
} }
if (userConfig) { if (userConfig) {