2 Commits

Author SHA1 Message Date
18338781771 0da3969bd6 用户映射关系 2026-03-03 15:07:13 +08:00
18338781771 ae9b82e683 获取fdesk对应的用户id 2026-03-03 14:50:41 +08:00
13 changed files with 199 additions and 109 deletions
+33
View File
@@ -3,6 +3,7 @@
namespace app\controller;
use app\model\User;
use app\model\UserSystemMapping;
use Webman\Http\Request;
use Webman\Http\Response;
@@ -130,4 +131,36 @@ class UserController extends BaseController
'page_size' => $pageSize,
]);
}
/**
* 获取用户在指定系统的映射ID
*/
public function systemMapping(Request $request): Response
{
$userId = $this->getUserId($request);
$systemCode = $request->get('system', 'fdesk');
// 获取用户信息以获取公司ID
$user = User::find($userId);
if (!$user) {
return $this->error('用户不存在');
}
// 查询系统映射
$mapping = UserSystemMapping::active()
->bySystem($systemCode)
->where('user_id', $userId)
->where('company_id', $user->company_id)
->first();
if (!$mapping) {
return $this->error('未找到系统映射信息,请联系管理员配置');
}
return $this->success([
'system_code' => $mapping->system_code,
'system_user_id' => $mapping->system_user_id,
'system_user_name' => $mapping->system_user_name,
]);
}
}
+89
View File
@@ -0,0 +1,89 @@
<?php
namespace app\model;
use support\Model;
class UserSystemMapping extends Model
{
protected $table = 'user_system_mapping';
/**
* 可填充字段
*/
protected $fillable = [
'user_id',
'company_id',
'system_code',
'system_user_id',
'system_user_name',
'status',
];
protected $casts = [
'user_id' => 'integer',
'company_id' => 'integer',
'status' => 'integer',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
/**
* 关联用户
*/
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
/**
* 关联公司
*/
public function company()
{
return $this->belongsTo(Company::class, 'company_id');
}
/**
* 获取有效的映射
*/
public function scopeActive($query)
{
return $query->where('status', 1);
}
/**
* 根据系统代码查找
*/
public function scopeBySystem($query, string $systemCode)
{
return $query->where('system_code', $systemCode);
}
/**
* 根据公司ID查找
*/
public function scopeByCompany($query, int $companyId)
{
return $query->where('company_id', $companyId);
}
/**
* 根据外部系统用户ID查找
*/
public function scopeBySystemUserId($query, string $systemUserId, string $systemCode = 'fdesk')
{
return $query->where('system_code', $systemCode)
->where('system_user_id', $systemUserId);
}
/**
* 获取用户在指定系统的映射
*/
public function scopeUserMapping($query, int $userId, int $companyId, string $systemCode = 'fdesk')
{
return $query->where('user_id', $userId)
->where('company_id', $companyId)
->where('system_code', $systemCode);
}
}
+1
View File
@@ -36,6 +36,7 @@ Route::group('/api/v1/user', function () {
Route::get('/menu', [app\controller\UserController::class, 'menu']);
Route::get('/companies', [app\controller\UserController::class, 'companies']);
Route::get('/list', [app\controller\UserController::class, 'list']);
Route::get('/system-mapping', [app\controller\UserController::class, 'systemMapping']);
Route::post('/switch-company', [app\controller\UserController::class, 'switchCompany']);
Route::post('/change-password', [app\controller\UserController::class, 'changePassword']);
})->middleware($authMiddleware);
+36
View File
@@ -900538,4 +900538,40 @@ INSERT INTO `users_copy1` VALUES (73, '833601', 1, '郭炯00078', 'https://s1-im
INSERT INTO `users_copy1` VALUES (74, '263364', 3, '李永春', 'https://s3-imfile.feishucdn.com/static-resource/v1/v3_00kb_ebbfbf17-c4b2-4306-8896-91ee3ef0b64g~?image_size=240x240&cut_type=&quality=&format=image&sticker_format=.webp', NULL, 'liyongchun@paikacar.com', '13073773542', '$2y$10$ed9m0Ecvj6FES2ntlRwedObN./Giv0.CmMZAvkKtA4dd2IB5PBbcm', 1, 1, 'on_bf01c7ea230962313781a54c927eba10', 2, 'x_server/user_signature/20260202/b9ce62914096acc13e12f16cc4740ef1_697ffc95e5338.png', '2026-01-29 09:50:17', '2026-02-26 16:18:51');
INSERT INTO `users_copy1` VALUES (75, '698484', 3, '曲江丽', 'https://s1-imfile.feishucdn.com/static-resource/v1/v3_00uh_6412ff58-77d6-4e5f-b35e-6b1a76ed31eg~?image_size=240x240&cut_type=&quality=&format=image&sticker_format=.webp', NULL, 'qujiangli@paikacar.com', '18339956922', '$2y$10$j9w0jkEYliIPbGbiPK5MSOhqGwwzY1Enf/koZC2MVb4JrXSFUhqDq', 1, 1, 'on_8f01d5bee7af07ce3de5e31df7075c99', 2, 'x_server/user_signature/20260210/6ad00820e9be180db7ac4aa729e4b6e9_698ad1464aae9.png', '2026-02-10 14:25:49', '2026-02-26 16:18:51');
-- ----------------------------
-- Table structure for user_system_mapping
-- ----------------------------
DROP TABLE IF EXISTS `user_system_mapping`;
CREATE TABLE `user_system_mapping` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
`user_id` bigint UNSIGNED NOT NULL COMMENT '本项目用户ID',
`company_id` bigint UNSIGNED NOT NULL COMMENT '公司ID',
`system_code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'fdesk' COMMENT '系统代码(如:fdesk, erp, crm)',
`system_user_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '外部系统用户ID',
`system_user_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '外部系统用户名(冗余字段,便于查看)',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1=有效,0=无效',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_user_company_system` (`user_id`, `company_id`, `system_code`) USING BTREE,
INDEX `idx_system_user_id`(`system_code`, `system_user_id`) USING BTREE,
INDEX `idx_company_id`(`company_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户系统映射表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user_system_mapping
-- ----------------------------
INSERT INTO `user_system_mapping` VALUES (1, 1, 2, 'fdesk', '1', '李佳博', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (2, 2, 2, 'fdesk', '74', '陈兰', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (3, 3, 2, 'fdesk', '78', '陈标', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (4, 4, 1, 'fdesk', '185', '闫继光', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (5, 76, 1, 'fdesk', '175', '王爽', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (6, 78, 3, 'fdesk', '175', '王爽', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (7, 79, 2, 'fdesk', '5', '闫继光', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (8, 82, 2, 'fdesk', '75', '范彩凤', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (9, 85, 1, 'fdesk', '183', '李冰杰', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (10, 88, 1, 'fdesk', '71', '崔琪', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (11, 90, 3, 'fdesk', '205', '李永春', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
INSERT INTO `user_system_mapping` VALUES (12, 92, 1, 'fdesk', '47', '朱海卫', 1, '2026-03-03 14:32:09', '2026-03-03 14:32:09');
SET FOREIGN_KEY_CHECKS = 1;
+35 -5
View File
@@ -1,8 +1,8 @@
# 数据库结构文档
**版本**: 1.1.0
**更新时间**: 2026-03-01
**状态**: 线上稳定版本
**版本**: 1.1.1
**更新时间**: 2026-03-03
**状态**: 开发中
---
@@ -358,6 +358,34 @@
---
### 15. user_system_mapping - 用户系统映射表
用于将本项目用户ID映射到外部系统(如fdesk、ERP、CRM等)的用户ID。
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| id | bigint unsigned | ✓ | AUTO_INCREMENT | 记录ID |
| user_id | bigint unsigned | ✓ | | 本项目用户ID |
| company_id | bigint unsigned | ✓ | | 公司ID |
| system_code | varchar(50) | ✓ | fdesk | 系统代码(如:fdesk, erp, crm) |
| system_user_id | varchar(100) | ✓ | | 外部系统用户ID |
| system_user_name | varchar(100) | | | 外部系统用户名(冗余字段) |
| status | tinyint | ✓ | 1 | 状态:1=有效,0=无效 |
| created_at | timestamp | ✓ | CURRENT_TIMESTAMP | 创建时间 |
| updated_at | timestamp | ✓ | CURRENT_TIMESTAMP | 更新时间 |
**索引**:
- PRIMARY KEY (`id`)
- UNIQUE KEY `uk_user_company_system` (`user_id`, `company_id`, `system_code`)
- KEY `idx_system_user_id` (`system_code`, `system_user_id`)
- KEY `idx_company_id` (`company_id`)
**说明**:
- 同一用户在同一公司下对应一个外部系统只能有一条记录
- 支持多个外部系统的映射关系
---
## 📊 表关系图
```
@@ -368,6 +396,8 @@ users (1) ----< (N) user_feishu_apps
+----> companies (1) ----< (N) company_feishu_apps
|
+----> user_sn (N)
|
+----> user_system_mapping (N)
users (1) ----< (N) business_trip
| |
@@ -419,5 +449,5 @@ users (1) ----< (N) business_trip
---
**维护人**: 开发团队
**最后更新**: 2026-03-01
**版本**: 1.1.0 (线上稳定版)
**最后更新**: 2026-03-03
**版本**: 1.1.1
+1 -6
View File
@@ -186,10 +186,5 @@
"lastMonth": "Last Month",
"thisYear": "This Year",
"lastYear": "Last Year"
},
"maintenance": {
"title": "System Upgrade",
"message": "The system is under maintenance. Expected to resume at 23:59 tonight. Please wait patiently.",
"estimatedTime": "Estimated recovery: 23:59 tonight"
}
}
}
+1 -6
View File
@@ -161,10 +161,5 @@
"lastMonth": "Mes Pasado",
"thisYear": "Este Año",
"lastYear": "Año Pasado"
},
"maintenance": {
"title": "Actualización del sistema",
"message": "El sistema está en mantenimiento. Se espera que se reanude a las 23:59 de esta noche. Por favor, espere pacientemente.",
"estimatedTime": "Hora estimada de recuperación: 23:59 esta noche"
}
}
}
+1 -6
View File
@@ -161,10 +161,5 @@
"lastMonth": "Mois Dernier",
"thisYear": "Cette Année",
"lastYear": "Année Dernière"
},
"maintenance": {
"title": "Mise à jour du système",
"message": "Le système est en maintenance. Reprise prévue à 23h59 ce soir. Veuillez patienter.",
"estimatedTime": "Heure de reprise estimée : 23h59 ce soir"
}
}
}
+1 -6
View File
@@ -161,10 +161,5 @@
"lastMonth": "В прошлом месяце",
"thisYear": "В этом году",
"lastYear": "В прошлом году"
},
"maintenance": {
"title": "Обновление системы",
"message": "Система находится на техническом обслуживании. Ожидаемое восстановление в 23:59 сегодня. Пожалуйста, подождите.",
"estimatedTime": "Ожидаемое восстановление: сегодня 23:59"
}
}
}
+1 -6
View File
@@ -186,10 +186,5 @@
"lastMonth": "上月",
"thisYear": "今年",
"lastYear": "去年"
},
"maintenance": {
"title": "系统升级中",
"message": "系统维护升级中,预计今晚 23:59 恢复,请耐心等待",
"estimatedTime": "预计恢复时间:今晚 23:59"
}
}
}
-19
View File
@@ -1,35 +1,16 @@
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { Button, Input, Toast } from 'antd-mobile'
import { useTranslation } from 'react-i18next'
import { useUserStore } from '@/stores/user'
import loginStyles from '../login/login.module.css'
import styles from './login-simple.module.css'
// 维护模式开关 - 恢复时改为 false 重新部署即可
const MAINTENANCE_MODE = true
export default function SimpleLogin() {
const { t } = useTranslation()
const navigate = useNavigate()
const { login } = useUserStore()
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(false)
if (MAINTENANCE_MODE) {
return (
<div className={loginStyles.login}>
<div className={loginStyles.maintenanceContainer}>
<div className={loginStyles.maintenanceIcon}>🔧</div>
<h1 className={loginStyles.maintenanceTitle}>{t('maintenance.title')}</h1>
<p className={loginStyles.maintenanceMessage}>{t('maintenance.message')}</p>
<p className={loginStyles.maintenanceTime}>{t('maintenance.estimatedTime')}</p>
</div>
</div>
)
}
const handleLogin = async () => {
if (!username || !password) {
Toast.show('请输入用户名和密码')
-18
View File
@@ -1,14 +1,10 @@
import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { Toast } from 'antd-mobile'
import { useTranslation } from 'react-i18next'
import { useUserStore } from '@/stores/user'
import QRCodeLogin from './QRCodeLogin'
import styles from './login.module.css'
// 维护模式开关 - 恢复时改为 false 重新部署即可
const MAINTENANCE_MODE = true
// 声明飞书 JSAPI 类型
declare global {
interface Window {
@@ -20,7 +16,6 @@ declare global {
}
export default function Login() {
const { t } = useTranslation()
const navigate = useNavigate()
const { login } = useUserStore()
const [loading, setLoading] = useState(true)
@@ -28,19 +23,6 @@ export default function Login() {
const [showQRCode, setShowQRCode] = useState(false)
const [shouldSkipAutoLogin, setShouldSkipAutoLogin] = useState(false) // 防止重复执行免登
if (MAINTENANCE_MODE) {
return (
<div className={styles.login}>
<div className={styles.maintenanceContainer}>
<div className={styles.maintenanceIcon}>🔧</div>
<h1 className={styles.maintenanceTitle}>{t('maintenance.title')}</h1>
<p className={styles.maintenanceMessage}>{t('maintenance.message')}</p>
<p className={styles.maintenanceTime}>{t('maintenance.estimatedTime')}</p>
</div>
</div>
)
}
// 检查登录状态
useEffect(() => {
const urlParams = new URLSearchParams(window.location.search)
-37
View File
@@ -102,40 +102,3 @@
transform: rotate(360deg);
}
}
.maintenanceContainer {
text-align: center;
padding: 40px 30px;
max-width: 360px;
}
.maintenanceIcon {
font-size: 64px;
margin-bottom: 24px;
animation: bounce 2s ease-in-out infinite;
}
.maintenanceTitle {
color: #fff;
font-size: 24px;
font-weight: 600;
margin: 0 0 16px 0;
}
.maintenanceMessage {
color: rgba(255, 255, 255, 0.9);
font-size: 16px;
line-height: 1.6;
margin: 0 0 12px 0;
}
.maintenanceTime {
color: rgba(255, 255, 255, 0.7);
font-size: 14px;
margin: 0;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}