mrDarker
2025-06-10 2f01b46f82188ea746024733cc3dd00286f30b6a
1. 迁移用户登录、管理等界面和数据
已添加16个文件
已修改4个文件
2748 ■■■■■ 文件已修改
SourceCode/Bond/Servo/CBaseDlg.cpp 341 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CBaseDlg.h 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ChangePasswordDlg.cpp 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ChangePasswordDlg.h 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/InputDialog.cpp 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/InputDialog.h 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/LoginDlg.cpp 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/LoginDlg.h 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.rc 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.vcxproj 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.vcxproj.filters 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/SystemLogManager.cpp 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/SystemLogManager.h 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/SystemLogManagerDlg.cpp 293 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/SystemLogManagerDlg.h 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/UserManager.cpp 542 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/UserManager.h 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/UserManagerDlg.cpp 546 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/UserManagerDlg.h 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/resource.h 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CBaseDlg.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,341 @@
#include "stdafx.h"
#include "CBaseDlg.h"
#include "GridCtrl.h"
// å…¨å±€ä¸»é¢˜å¯¹è±¡
Theme g_lightTheme = { RGB(255, 255, 255), RGB(0, 0, 0), RGB(240, 240, 240), RGB(200, 200, 200) };
Theme g_darkTheme = { RGB(40, 40, 40), RGB(255, 255, 255), RGB(60, 60, 60), RGB(80, 80, 80) };
CFont g_defaultFont;
Theme* g_currentTheme = &g_lightTheme;
// å…¨å±€èµ„源句柄
HINSTANCE g_hCurrentResource = NULL;
IMPLEMENT_DYNAMIC(CBaseDlg, CDialogEx)
CBaseDlg::CBaseDlg(UINT nID, CWnd* pPage) : CDialogEx(nID, pPage), m_bResizing(false)
{
    m_nID = nID;
    m_pParent = pPage;
    m_nInitialWidth = 0;
    m_nInitialHeight = 0;
}
CBaseDlg::~CBaseDlg()
{
    // shared_ptr会自动清理内存,不需要手动删除
    m_mapFonts.clear();
    m_mapCtrlLayouts.clear();
    m_mapControls.clear();
}
void CBaseDlg::SwitchTheme(ThemeType enThemeType)
{
    // ä½¿ç”¨ map æ¥æ ¹æ® themeType æŸ¥æ‰¾ä¸»é¢˜
    static const std::unordered_map<ThemeType, Theme*> themeMap = {
        { ThemeType::Light, &g_lightTheme },
        { ThemeType::Dark, &g_darkTheme }
    };
    // è®¾ç½®å½“前主题
    auto it = themeMap.find(enThemeType);
    if (it != themeMap.end()) {
        g_currentTheme = it->second;
    }
    else {
        g_currentTheme = &g_lightTheme;
    }
    // æ›´æ–°æŽ§ä»¶çš„外观
    CWnd* pWnd = GetWindow(GW_CHILD);
    while (pWnd) {
        pWnd->Invalidate();
        pWnd = pWnd->GetNextWindow();
    }
    // æ›´æ–°å¯¹è¯æ¡†èƒŒæ™¯é¢œè‰²
    SetBackgroundColor(g_currentTheme->backgroundColor);
}
void CBaseDlg::LoadResourceLibrary(const CString& strLanguage)
{
    // å¸è½½ä¹‹å‰åŠ è½½çš„èµ„æºåº“
    UnloadResourceLibrary();
    // åŠ è½½æ–°çš„èµ„æºåº“
    g_hCurrentResource = AfxLoadLibrary(strLanguage);
    // è®¾ç½®æ–°çš„资源句柄
    if (g_hCurrentResource != NULL) {
        AfxSetResourceHandle(g_hCurrentResource);
    }
}
void CBaseDlg::UnloadResourceLibrary()
{
    // å¸è½½ä¹‹å‰åŠ è½½çš„èµ„æºåº“
    if (g_hCurrentResource != NULL) {
        FreeLibrary(g_hCurrentResource);    // é‡Šæ”¾å½“前资源库
        g_hCurrentResource = NULL;            // æ¸…空资源句柄
    }
}
CFont* CBaseDlg::GetOrCreateFont(int nFontSize)
{
    auto it = m_mapFonts.find(nFontSize);
    if (it != m_mapFonts.end()) {
        return it->second.get();
    }
    // ä½¿ç”¨ shared_ptr æ¥ç®¡ç†å­—体对象
    auto font = std::make_shared<CFont>();
    LOGFONT logFont = { 0 };
    _tcscpy_s(logFont.lfFaceName, _T("Segoe UI"));
    logFont.lfHeight = -nFontSize;
    logFont.lfQuality = CLEARTYPE_QUALITY;
    font->CreateFontIndirect(&logFont);
    m_mapFonts[nFontSize] = font;
    return font.get();
}
void CBaseDlg::SetDefaultFont()
{
    CFont* defaultFont = GetOrCreateFont(12);
    CWnd* pWnd = GetWindow(GW_CHILD);
    while (pWnd) {
        TCHAR szClassName[256];
        GetClassName(pWnd->m_hWnd, szClassName, sizeof(szClassName));
        if (_tcsicmp(szClassName, _T("MFCGridCtrl")) == 0) {
            pWnd = pWnd->GetNextWindow();
            continue;
        }
        pWnd->SetFont(defaultFont, TRUE);
        pWnd = pWnd->GetNextWindow();
    }
}
BOOL CBaseDlg::AddControl(UINT nCtrlID, CWnd* pControl)
{
    // ç¡®ä¿æŽ§ä»¶ä¸é‡å¤æ·»åŠ 
    if (m_mapControls.find(nCtrlID) != m_mapControls.end()) {
        return FALSE;  // æŽ§ä»¶å·²ç»å­˜åœ¨
    }
    m_mapControls[nCtrlID] = std::unique_ptr<CWnd>(pControl);
    return TRUE;
}
BOOL CBaseDlg::RemoveControl(UINT nCtrlID)
{
    auto it = m_mapControls.find(nCtrlID);
    if (it != m_mapControls.end()) {
        m_mapControls.erase(it);
        return TRUE;
    }
    return FALSE;
}
BOOL CBaseDlg::UpdateControlText(UINT nCtrlID, const CString& strText)
{
    auto it = m_mapControls.find(nCtrlID);
    if (it != m_mapControls.end()) {
        CWnd* pWnd = it->second.get();
        if (pWnd->GetSafeHwnd() != nullptr)
        {
            pWnd->SetWindowText(strText);
            return TRUE;
        }
    }
    return FALSE;
}
CWnd* CBaseDlg::GetControl(UINT nCtrlID)
{
    auto it = m_mapControls.find(nCtrlID);
    if (it != m_mapControls.end()) {
        return it->second.get();
    }
    return nullptr;
}
void CBaseDlg::AdjustControls(float dScaleX, float dScaleY)
{
    if (m_bResizing) return; // é˜²æ­¢åœ¨è°ƒæ•´è¿‡ç¨‹ä¸­é‡å¤è°ƒæ•´
    m_bResizing = true;
    CWnd* pWnd = GetWindow(GW_CHILD);
    while (pWnd) {
        int nCtrlID = pWnd->GetDlgCtrlID();
        if (nCtrlID != -1 && m_mapCtrlLayouts.find(nCtrlID) != m_mapCtrlLayouts.end()) {
            CRect originalRect = m_mapCtrlLayouts[nCtrlID];
            CRect newRect(
                static_cast<int>(originalRect.left * dScaleX),
                static_cast<int>(originalRect.top * dScaleY),
                static_cast<int>(originalRect.right * dScaleX),
                static_cast<int>(originalRect.bottom * dScaleY));
            TCHAR szClassName[256];
            GetClassName(pWnd->m_hWnd, szClassName, sizeof(szClassName));
            if (_tcsicmp(szClassName, _T("ComboBox")) == 0) {
                CComboBox* pComboBox = (CComboBox*)pWnd;
                pComboBox->SetItemHeight(-1, newRect.Height());  // -1 è¡¨ç¤ºæ‰€æœ‰é¡¹çš„高度
            }
            if (_tcsicmp(szClassName, _T("MFCGridCtrl")) == 0) {
                CGridCtrl* pGridCtrl = (CGridCtrl*)pWnd;
                pGridCtrl->SetDefCellHeight(newRect.Height() / 21);
                pGridCtrl->ExpandColumnsToFit(TRUE);
                pGridCtrl->ExpandLastColumn();
                pGridCtrl->Invalidate();
                pGridCtrl->UpdateWindow();
            }
            pWnd->MoveWindow(&newRect);
            AdjustControlFont(pWnd, newRect.Width(), newRect.Height());
        }
        pWnd = pWnd->GetNextWindow();
    }
    m_bResizing = false;
}
void CBaseDlg::AdjustControlFont(CWnd* pWnd, int nWidth, int nHeight)
{
    TCHAR szClassName[256];
    GetClassName(pWnd->m_hWnd, szClassName, sizeof(szClassName));
    if (_tcsicmp(szClassName, _T("MFCGridCtrl")) == 0) {
        return;
    }
    int fontSize = nHeight / 2;
    if (fontSize < 8) fontSize = 8;
    if (fontSize > 32) fontSize = 32;
    CFont* pFont = GetOrCreateFont(fontSize);
    pWnd->SetFont(pFont);
    pWnd->Invalidate(); // åˆ·æ–°æŽ§ä»¶æ˜¾ç¤º
}
BEGIN_MESSAGE_MAP(CBaseDlg, CDialogEx)
    ON_WM_SIZE()
    ON_WM_GETMINMAXINFO()
    ON_WM_CTLCOLOR()
END_MESSAGE_MAP()
BOOL CBaseDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    // èŽ·å–å½“å‰è¯­è¨€
    LANGID langId = GetUserDefaultLangID();
    if (langId == LANG_CHINESE) {
        // åŠ è½½ä¸­æ–‡èµ„æº
    }
    else {
        // åŠ è½½è‹±æ–‡èµ„æº
    }
    // èŽ·å–å¯¹è¯æ¡†çš„å·¥ä½œåŒºï¼ˆå±å¹•å¯ç”¨åŒºåŸŸï¼‰
    CRect screenRect, dlgRect, clientRect;
    SystemParametersInfo(SPI_GETWORKAREA, 0, &screenRect, 0);
    GetClientRect(&clientRect);
    m_nInitialWidth = clientRect.Width();
    m_nInitialHeight = clientRect.Height();
    // è®¾ç½®é»˜è®¤å­—体
    CFont* pDefaultFont = GetOrCreateFont(12);
    // éåŽ†å­çª—å£ï¼ˆæŽ§ä»¶ï¼‰
    CWnd* pWnd = GetWindow(GW_CHILD);
    while (pWnd) {
        int nCtrlID = pWnd->GetDlgCtrlID();
        if (nCtrlID != -1) {
            // ä¿å­˜æŽ§ä»¶çš„初始布局
            CRect ctrlRect;
            pWnd->GetWindowRect(&ctrlRect);
            ScreenToClient(&ctrlRect);
            m_mapCtrlLayouts[nCtrlID] = ctrlRect;
            // æŽ’除不需要操作的控件(如自定义控件 GridCtrl)
            TCHAR szClassName[256];
            GetClassName(pWnd->m_hWnd, szClassName, sizeof(szClassName));
            if (_tcsicmp(szClassName, _T("MFCGridCtrl")) == 0) {
                pWnd = pWnd->GetNextWindow();
                continue;
            }
            // è®¾ç½®æŽ§ä»¶çš„默认字体
            pWnd->SetFont(pDefaultFont);
        }
        pWnd = pWnd->GetNextWindow();
    }
    // å°†å¯¹è¯æ¡†å±…中
    GetWindowRect(&dlgRect);
    int dlgWidth = dlgRect.Width() * 2;
    int dlgHeight = dlgRect.Height() * 2;
    if (dlgWidth > screenRect.Width()) {
        dlgWidth = screenRect.Width();
    }
    if (dlgHeight > screenRect.Height()) {
        dlgHeight = screenRect.Height();
    }
    int centerX = screenRect.left + (screenRect.Width() - dlgWidth) / 2;
    int centerY = screenRect.top + (screenRect.Height() - dlgHeight) / 2;
    MoveWindow(centerX, centerY, dlgWidth, dlgHeight);
    return TRUE;
}
void CBaseDlg::OnSize(UINT nType, int cx, int cy)
{
    CDialogEx::OnSize(nType, cx, cy);
    if (nType == SIZE_MINIMIZED || m_mapCtrlLayouts.empty()) {
        return;
    }
    // æ£€æŸ¥å°ºå¯¸å˜åŒ–是否足够大,避免频繁调整
    //static int lastWidth = 0, lastHeight = 0;
    //if (abs(cx - lastWidth) < 10 && abs(cy - lastHeight) < 10) {
    //    return;
    //}
    //lastWidth = cx;
    //lastHeight = cy;
    // è®¡ç®—比例并调整布局
    float dScaleX = static_cast<float>(cx) / m_nInitialWidth;
    float dScaleY = static_cast<float>(cy) / m_nInitialHeight;
    AdjustControls(dScaleX, dScaleY);
}
void CBaseDlg::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
    lpMMI->ptMinTrackSize.x = 400;
    lpMMI->ptMinTrackSize.y = 300;
    CDialogEx::OnGetMinMaxInfo(lpMMI);
}
HBRUSH CBaseDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    if (g_currentTheme) {
        pDC->SetBkColor(g_currentTheme->backgroundColor);
        pDC->SetTextColor(g_currentTheme->textColor);
        // è¿”回背景画刷
        return CreateSolidBrush(g_currentTheme->backgroundColor);
    }
    return CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
}
SourceCode/Bond/Servo/CBaseDlg.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
#pragma once
#include <map>
#include <memory>
#include <unordered_map>
enum class ThemeType {
    Light,  // æµ…色主题
    Dark    // æ·±è‰²ä¸»é¢˜
};
struct Theme {
    COLORREF backgroundColor;
    COLORREF textColor;
    COLORREF buttonColor;
    COLORREF borderColor;
};
class CBaseDlg : public CDialogEx
{
    DECLARE_DYNAMIC(CBaseDlg)
public:
    CBaseDlg(UINT nID, CWnd* pPage);            // æ ‡å‡†æž„造函数
    virtual ~CBaseDlg();                        // æžæž„函数
    // ä¸»é¢˜ç®¡ç†
    void SwitchTheme(ThemeType enThemeType);            // åˆ‡æ¢ä¸»é¢˜
    // èµ„源管理
    void LoadResourceLibrary(const CString& strLanguage);            // åŠ è½½èµ„æºåº“
    void UnloadResourceLibrary();                                    // å¸è½½èµ„源库
    // å­—体管理
    CFont* GetOrCreateFont(int nFontSize);            // èŽ·å–æˆ–åˆ›å»ºå­—ä½“
    void SetDefaultFont();                            // è®¾ç½®é»˜è®¤å­—体
    // åŠ¨æ€æŽ§ä»¶ç®¡ç†
    BOOL AddControl(UINT nCtrlID, CWnd* pControl);                    // æ·»åŠ æŽ§ä»¶
    BOOL RemoveControl(UINT nCtrlID);                                // ç§»é™¤æŽ§ä»¶
    BOOL UpdateControlText(UINT nCtrlID, const CString& strText);   // æ›´æ–°æŽ§ä»¶æ–‡æœ¬
    CWnd* GetControl(UINT nCtrlID);                                    // èŽ·å–æŽ§ä»¶
private:
    void AdjustControls(float dScaleX, float dScaleY);                // è°ƒæ•´æŽ§ä»¶å¤§å°
    void AdjustControlFont(CWnd* pWnd, int nWidth, int nHeight);    // è°ƒæ•´æŽ§ä»¶å­—体
private:
    UINT m_nID;                                                    // å¯¹è¯æ¡†ID
    CWnd* m_pParent;                                            // çˆ¶çª—口
    bool m_bResizing;                                            // æŽ§ä»¶æ˜¯å¦æ­£åœ¨è°ƒæ•´å¤§å°
    int m_nInitialWidth;                                        // å¯¹è¯æ¡†åˆå§‹å®½åº¦
    int m_nInitialHeight;                                        // å¯¹è¯æ¡†åˆå§‹é«˜åº¦
    std::unordered_map<int, CRect> m_mapCtrlLayouts;            // æŽ§ä»¶å¸ƒå±€
    std::map<UINT, std::unique_ptr<CWnd>> m_mapControls;        // æŽ§ä»¶é›†åˆ
    std::unordered_map<int, std::shared_ptr<CFont>> m_mapFonts;    // æŽ§ä»¶å­—体
    DECLARE_MESSAGE_MAP()
public:
    virtual BOOL OnInitDialog();
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg void OnGetMinMaxInfo(MINMAXINFO* lpMMI);
    afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
};
SourceCode/Bond/Servo/ChangePasswordDlg.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
// CChangePasswordDlg.cpp: å®žçŽ°æ–‡ä»¶
//
#include "stdafx.h"
#include "Servo.h"
#include "afxdialogex.h"
#include "ChangePasswordDlg.h"
#include "UserManager.h"
#include "SystemLogManager.h"
// CChangePasswordDlg å¯¹è¯æ¡†
IMPLEMENT_DYNAMIC(CChangePasswordDlg, CDialogEx)
CChangePasswordDlg::CChangePasswordDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_DIALOG_CHANGE_PASSWORD, pParent)
{
}
CChangePasswordDlg::~CChangePasswordDlg()
{
}
bool CChangePasswordDlg::changeUserPassword(UserManager& userManager, const std::string& username, const std::string& newPassword)
{
    if (!userManager.changePassword(username, newPassword)) {
        AfxMessageBox(_T("修改用户密码失败。"));
        return false;
    }
    return true;
}
void CChangePasswordDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_EDIT_CHANGE_USERNAME, m_editUsername);
    DDX_Control(pDX, IDC_EDIT_CHANGE_CURRENT_PASSWORD, m_editCurrentPassword);
    DDX_Control(pDX, IDC_EDIT_CHANGE_NEW_PASSWORD, m_editNewPassword);
    DDX_Control(pDX, IDC_EDIT_CHANGE_CONFIRM_PASSWORD, m_editConfirmPassword);
}
BEGIN_MESSAGE_MAP(CChangePasswordDlg, CDialogEx)
    ON_BN_CLICKED(IDOK, &CChangePasswordDlg::OnBnClickedOk)
END_MESSAGE_MAP()
// CChangePasswordDlg æ¶ˆæ¯å¤„理程序
BOOL CChangePasswordDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    // è®¾ç½®çª—口标题和初始值
    SetWindowText(_T("修改密码"));
    UserManager& userManager = UserManager::getInstance();
    if (userManager.isLoggedIn()) {
        m_editUsername.SetWindowText(userManager.getCurrentUser().c_str());
    }
    return TRUE;
}
void CChangePasswordDlg::OnBnClickedOk()
{
    CString username, currentPassword, newPassword, confirmPassword;
    m_editUsername.GetWindowText(username);
    m_editCurrentPassword.GetWindowText(currentPassword);
    m_editNewPassword.GetWindowText(newPassword);
    m_editConfirmPassword.GetWindowText(confirmPassword);
    if (username.IsEmpty() || currentPassword.IsEmpty() || newPassword.IsEmpty() || confirmPassword.IsEmpty()) {
        AfxMessageBox(_T("所有字段均为必填项。"));
        return;
    }
    if (newPassword.Compare(confirmPassword) != 0) {
        AfxMessageBox(_T("新密码与确认密码不匹配。"));
        return;
    }
    SystemLogManager& logManager = SystemLogManager::getInstance();
    if (newPassword.Compare(currentPassword) == 0) {
        EndDialog(IDCANCEL);
        return;
    }
#ifdef UNICODE
    std::string strUsername = CStringA(username);
    std::string strCurrentPassword = CStringA(currentPassword);
    std::string strNewPassword = CStringA(newPassword);
#else
    std::string strUsername = username;
    std::string strCurrentPassword = currentPassword;
    std::string strNewPassword = newPassword;
#endif
    UserManager& userManager = UserManager::getInstance();
    if (userManager.isLoggedIn() && strCurrentPassword.compare(userManager.getCurrentPass()) == 0) {
        if (changeUserPassword(userManager, strUsername, strNewPassword)) {
            EndDialog(IDOK);
            logManager.log(SystemLogManager::LogType::Info, "修改密码成功!", strUsername);
        }
    }
    else {
        if (userManager.login(strUsername, strCurrentPassword)) {
            if (changeUserPassword(userManager, strUsername, strNewPassword)) {
                EndDialog(IDOK);
                logManager.log(SystemLogManager::LogType::Info, "修改密码成功!", strUsername);
            }
            else {
                userManager.logout();
            }
        }
        else {
            AfxMessageBox(_T("当前用户名或密码输入错误。"));
        }
    }
}
SourceCode/Bond/Servo/ChangePasswordDlg.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
#pragma once
#include "afxdialogex.h"
#include <string>
class UserManager;
// CChangePasswordDlg å¯¹è¯æ¡†
class CChangePasswordDlg : public CDialogEx
{
    DECLARE_DYNAMIC(CChangePasswordDlg)
public:
    CChangePasswordDlg(CWnd* pParent = nullptr);   // æ ‡å‡†æž„造函数
    virtual ~CChangePasswordDlg();
private:
    bool changeUserPassword(UserManager& userManager, const std::string& username, const std::string& newPassword);
// å¯¹è¯æ¡†æ•°æ®
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_DIALOG_CHANGE_PASSWORD };
#endif
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV æ”¯æŒ
    virtual BOOL OnInitDialog();
    afx_msg void OnBnClickedOk();
    afx_msg void OnBnClickedCancel();
    DECLARE_MESSAGE_MAP()
private:
    CEdit m_editUsername;              // ç”¨æˆ·åè¾“入框
    CEdit m_editCurrentPassword;       // å½“前密码输入框
    CEdit m_editNewPassword;           // æ–°å¯†ç è¾“入框
    CEdit m_editConfirmPassword;       // ç¡®è®¤æ–°å¯†ç è¾“入框
};
SourceCode/Bond/Servo/InputDialog.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,70 @@
// InputDialog.cpp: å®žçŽ°æ–‡ä»¶
//
#include "stdafx.h"
#include "Servo.h"
#include "afxdialogex.h"
#include "InputDialog.h"
// CInputDialog å¯¹è¯æ¡†
IMPLEMENT_DYNAMIC(CInputDialog, CDialogEx)
CInputDialog::CInputDialog(const CString& strTitle, const CString& strPrompt, CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_DIALOG_INPUT, pParent), m_strTitle(strTitle), m_strPrompt(strPrompt), m_strInput(_T(""))
{
}
CInputDialog::~CInputDialog()
{
}
CString CInputDialog::GetInputText() const
{
    return m_strInput;
}
void CInputDialog::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_EDIT_INPUT, m_editInput);
    DDX_Control(pDX, IDC_STATIC_PROMPT, m_staticPrompt);
}
BEGIN_MESSAGE_MAP(CInputDialog, CDialogEx)
    ON_BN_CLICKED(IDOK, &CInputDialog::OnBnClickedOk)
END_MESSAGE_MAP()
// CInputDialog æ¶ˆæ¯å¤„理程序
BOOL CInputDialog::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    // TODO:  åœ¨æ­¤æ·»åŠ é¢å¤–çš„åˆå§‹åŒ–
    SetWindowText(m_strTitle);
    m_staticPrompt.SetWindowText(m_strPrompt);
    m_editInput.SetFocus();
    m_editInput.SetWindowText("1");
    return TRUE;  // return TRUE unless you set the focus to a control
    // å¼‚常: OCX å±žæ€§é¡µåº”返回 FALSE
}
void CInputDialog::OnBnClickedOk()
{
    // TODO: åœ¨æ­¤æ·»åŠ æŽ§ä»¶é€šçŸ¥å¤„ç†ç¨‹åºä»£ç 
    m_editInput.GetWindowText(m_strInput);
    if (m_strInput.IsEmpty()) {
        AfxMessageBox(_T("输入不能为空!"));
        return;
    }
    CDialogEx::OnOK();
}
SourceCode/Bond/Servo/InputDialog.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
#pragma once
#include "afxdialogex.h"
// CInputDialog å¯¹è¯æ¡†
class CInputDialog : public CDialogEx
{
    DECLARE_DYNAMIC(CInputDialog)
public:
    CInputDialog(const CString& strTitle, const CString& strPrompt, CWnd* pParent = nullptr);
    virtual ~CInputDialog();
    CString GetInputText() const; // èŽ·å–è¾“å…¥å†…å®¹
// å¯¹è¯æ¡†æ•°æ®
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_DIALOG_INPUT };
#endif
private:
    CEdit m_editInput;
    CStatic m_staticPrompt;
    CString m_strTitle;   // å¯¹è¯æ¡†æ ‡é¢˜
    CString m_strPrompt;  // æç¤ºæ–‡æœ¬
    CString m_strInput;   // è¾“入的文本
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV æ”¯æŒ
    DECLARE_MESSAGE_MAP()
public:
    virtual BOOL OnInitDialog();
    afx_msg void OnBnClickedOk();
};
SourceCode/Bond/Servo/LoginDlg.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,126 @@
// LoginDlg.cpp: å®žçŽ°æ–‡ä»¶
//
#include "stdafx.h"
#include "Servo.h"
#include "afxdialogex.h"
#include "LoginDlg.h"
#include "ChangePasswordDlg.h"
#include "UserManager.h"
#include "SystemLogManager.h"
// CLoginDlg å¯¹è¯æ¡†
IMPLEMENT_DYNAMIC(CLoginDlg, CDialogEx)
CLoginDlg::CLoginDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_DIALOG_LOGIN, pParent)
{
}
CLoginDlg::~CLoginDlg()
{
}
void CLoginDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_STATIC_IMAGE, m_staticImage);
    DDX_Control(pDX, IDC_EDIT_USERNAME, m_editUsername);
    DDX_Control(pDX, IDC_EDIT_PASSWORD, m_editPassword);
    DDX_Control(pDX, IDC_CHECK_REMEMBER_PASSWORD, m_checkRememberPassword);
}
BEGIN_MESSAGE_MAP(CLoginDlg, CDialogEx)
    ON_BN_CLICKED(IDC_BUTTON_LOGIN, &CLoginDlg::OnBnClickedLogin)
    ON_STN_CLICKED(IDC_STATIC_CHANGE_PASSWORD, &CLoginDlg::OnBnClickedChangePassword)
END_MESSAGE_MAP()
// CLoginDlg æ¶ˆæ¯å¤„理程序
BOOL CLoginDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    // è®¾ç½®çª—口标题和初始值
    SetWindowText(_T("登录"));
    CString strIconPath;
    strIconPath.Format(_T("%s\\Res\\Operator_High_32.ico"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir);
    // åŠ è½½å›¾æ ‡
    HICON hIcon = (HICON)::LoadImage(
        nullptr,
        strIconPath,
        IMAGE_ICON,
        32, // å›¾æ ‡å®½åº¦
        32, // å›¾æ ‡é«˜åº¦
        LR_LOADFROMFILE);
    if (hIcon) {
        // è®¾ç½® CStatic æŽ§ä»¶ä¸ºå›¾æ ‡æ ·å¼
        m_staticImage.ModifyStyle(0xF, SS_ICON);
        m_staticImage.SetIcon(hIcon);
    }
    // æ·»åŠ SS_NOTIFY样式
    CStatic* pStatic = (CStatic*)GetDlgItem(IDC_STATIC_CHANGE_PASSWORD);
    if (pStatic != nullptr) {
        pStatic->ModifyStyle(0, SS_NOTIFY);
    }
    UserManager& userManager = UserManager::getInstance();
    if (userManager.isLoggedIn()) {
        if (userManager.isRememberMe()) {
            m_checkRememberPassword.SetCheck(BST_CHECKED);
        }
        userManager.getCurrentUserRole();
        m_editUsername.SetWindowText(userManager.getCurrentUser().c_str());
        m_editPassword.SetWindowText(userManager.getCurrentPass().c_str());
    }
    return TRUE;
}
void CLoginDlg::OnBnClickedLogin()
{
    CString username, password, role;
    m_editUsername.GetWindowText(username);
    m_editPassword.GetWindowText(password);
    if (username.IsEmpty() || password.IsEmpty()) {
        AfxMessageBox(_T("请输入用户名和密码。"));
        return;
    }
#ifdef UNICODE
    std::string strUsername = CStringA(username);
    std::string strPassword = CStringA(password);
#else
    std::string strUsername = username;
    std::string strPassword = password;
#endif
    UserManager& userManager = UserManager::getInstance();
    SystemLogManager& logManager = SystemLogManager::getInstance();
    if (!userManager.login(strUsername, strPassword, (m_checkRememberPassword.GetCheck() == BST_CHECKED))) {
        AfxMessageBox(_T("登录失败。"));
        return;
    }
    EndDialog(IDOK);
    logManager.log(SystemLogManager::LogType::Info, _T("登录成功..."));
}
void CLoginDlg::OnBnClickedChangePassword()
{
    CChangePasswordDlg changePasswordDlg;
    if (changePasswordDlg.DoModal() == IDOK) {
        m_editPassword.SetWindowText("");
    }
}
SourceCode/Bond/Servo/LoginDlg.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
#pragma once
#include "afxdialogex.h"
// CLoginDlg å¯¹è¯æ¡†
class CLoginDlg : public CDialogEx
{
    DECLARE_DYNAMIC(CLoginDlg)
public:
    CLoginDlg(CWnd* pParent = nullptr);   // æ ‡å‡†æž„造函数
    virtual ~CLoginDlg();
// å¯¹è¯æ¡†æ•°æ®
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_DIALOG_LOGIN };
#endif
private:
    CStatic m_staticImage;
    CEdit m_editUsername;
    CEdit m_editPassword;
    CButton m_checkRememberPassword;
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV æ”¯æŒ
    virtual BOOL OnInitDialog();
    afx_msg void OnBnClickedLogin();
    afx_msg void OnBnClickedChangePassword();
    DECLARE_MESSAGE_MAP()
};
SourceCode/Bond/Servo/Servo.rc
Binary files differ
SourceCode/Bond/Servo/Servo.vcxproj
@@ -198,8 +198,10 @@
    <Text Include="ReadMe.txt" />
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="CBaseDlg.h" />
    <ClInclude Include="CEquipmentPage3.h" />
    <ClInclude Include="CGlassPool.h" />
    <ClInclude Include="ChangePasswordDlg.h" />
    <ClInclude Include="CMyStatusbar.h" />
    <ClInclude Include="CRobotCmdContainerDlg.h" />
    <ClInclude Include="CRobotCmdTestDlg.h" />
@@ -223,7 +225,9 @@
    <ClInclude Include="CRobotTask.h" />
    <ClInclude Include="CSlot.h" />
    <ClInclude Include="HorizontalLine.h" />
    <ClInclude Include="InputDialog.h" />
    <ClInclude Include="JobSlotGrid.h" />
    <ClInclude Include="LoginDlg.h" />
    <ClInclude Include="MsgDlg.h" />
    <ClInclude Include="PageRecipe.h" />
    <ClInclude Include="CDoubleGlass.h" />
@@ -321,16 +325,22 @@
    <ClInclude Include="ServoGraph.h" />
    <ClInclude Include="ServoMemDC.h" />
    <ClInclude Include="stdafx.h" />
    <ClInclude Include="SystemLogManager.h" />
    <ClInclude Include="SystemLogManagerDlg.h" />
    <ClInclude Include="targetver.h" />
    <ClInclude Include="TerminalDisplayDlg.h" />
    <ClInclude Include="ToolUnits.h" />
    <ClInclude Include="TransferManager.h" />
    <ClInclude Include="TopToolbar.h" />
    <ClInclude Include="UserManager.h" />
    <ClInclude Include="UserManagerDlg.h" />
    <ClInclude Include="VerticalLine.h" />
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="CBaseDlg.cpp" />
    <ClCompile Include="CEquipmentPage3.cpp" />
    <ClCompile Include="CGlassPool.cpp" />
    <ClCompile Include="ChangePasswordDlg.cpp" />
    <ClCompile Include="CMyStatusbar.cpp" />
    <ClCompile Include="CRobotCmdContainerDlg.cpp" />
    <ClCompile Include="CRobotCmdTestDlg.cpp" />
@@ -352,7 +362,9 @@
    <ClCompile Include="CRobotTask.cpp" />
    <ClCompile Include="CSlot.cpp" />
    <ClCompile Include="HorizontalLine.cpp" />
    <ClCompile Include="InputDialog.cpp" />
    <ClCompile Include="JobSlotGrid.cpp" />
    <ClCompile Include="LoginDlg.cpp" />
    <ClCompile Include="MsgDlg.cpp" />
    <ClCompile Include="PageRecipe.cpp" />
    <ClCompile Include="CDoubleGlass.cpp" />
@@ -452,10 +464,14 @@
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
    </ClCompile>
    <ClCompile Include="SystemLogManager.cpp" />
    <ClCompile Include="SystemLogManagerDlg.cpp" />
    <ClCompile Include="TerminalDisplayDlg.cpp" />
    <ClCompile Include="ToolUnits.cpp" />
    <ClCompile Include="TransferManager.cpp" />
    <ClCompile Include="TopToolbar.cpp" />
    <ClCompile Include="UserManager.cpp" />
    <ClCompile Include="UserManagerDlg.cpp" />
    <ClCompile Include="VerticalLine.cpp" />
  </ItemGroup>
  <ItemGroup>
SourceCode/Bond/Servo/Servo.vcxproj.filters
@@ -158,6 +158,14 @@
    <ClCompile Include="CRobotTaskDlg.cpp" />
    <ClCompile Include="PageTransferLog.cpp" />
    <ClCompile Include="ServoMemDC.cpp" />
    <ClCompile Include="ChangePasswordDlg.cpp" />
    <ClCompile Include="LoginDlg.cpp" />
    <ClCompile Include="SystemLogManagerDlg.cpp" />
    <ClCompile Include="UserManagerDlg.cpp" />
    <ClCompile Include="CBaseDlg.cpp" />
    <ClCompile Include="SystemLogManager.cpp" />
    <ClCompile Include="UserManager.cpp" />
    <ClCompile Include="InputDialog.cpp" />
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="AlarmManager.h" />
@@ -321,6 +329,14 @@
    <ClInclude Include="CRobotTaskDlg.h" />
    <ClInclude Include="PageTransferLog.h" />
    <ClInclude Include="ServoMemDC.h" />
    <ClInclude Include="ChangePasswordDlg.h" />
    <ClInclude Include="LoginDlg.h" />
    <ClInclude Include="SystemLogManagerDlg.h" />
    <ClInclude Include="UserManagerDlg.h" />
    <ClInclude Include="CBaseDlg.h" />
    <ClInclude Include="UserManager.h" />
    <ClInclude Include="SystemLogManager.h" />
    <ClInclude Include="InputDialog.h" />
  </ItemGroup>
  <ItemGroup>
    <ResourceCompile Include="Servo.rc" />
SourceCode/Bond/Servo/SystemLogManager.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,207 @@
#include "stdafx.h"
#include "SystemLogManager.h"
#include "UserManager.h"
#include <sstream>
#include <iostream>
#include <stdexcept>
#include <ctime>
// é™æ€æˆå‘˜åˆå§‹åŒ–
std::mutex SystemLogManager::m_mutex;
// èŽ·å–å•ä¾‹å®žä¾‹
SystemLogManager& SystemLogManager::getInstance() {
    static SystemLogManager instance;
    return instance;
}
// æž„造函数
SystemLogManager::SystemLogManager() : m_pDB(nullptr) {}
// æžæž„函数
SystemLogManager::~SystemLogManager() {
    m_pDB = nullptr; // æ¸…除指针引用
}
// è®¾ç½®æ•°æ®åº“连接
void SystemLogManager::setDatabase(BL::Database* db) {
    std::lock_guard<std::mutex> lock(m_mutex);
    m_pDB = db;
}
// åˆå§‹åŒ–日志表
bool SystemLogManager::initializeLogTable() {
    if (!m_pDB) {
        throw std::runtime_error("Database connection is not set.");
    }
    const std::string createTableQuery = R"(
        CREATE TABLE IF NOT EXISTS system_logs (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            log_type TEXT NOT NULL,
            event TEXT NOT NULL,
            username TEXT NOT NULL,
            timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
        )
    )";
    return m_pDB->executeQuery(createTableQuery);
}
// æ·»åŠ æ—¥å¿—ï¼ˆä½¿ç”¨å½“å‰ç”¨æˆ·ï¼‰
bool SystemLogManager::log(LogType logType, const std::string& event) {
    if (!m_pDB) {
        throw std::runtime_error("Database connection is not set.");
    }
    cleanOldLogs();
    std::string username = UserManager::getInstance().getCurrentUser();
    if (username.empty()) {
        username = "SYSTEM";
    }
    std::ostringstream query;
    query << "INSERT INTO system_logs (log_type, event, username) VALUES ("
        << "'" << logTypeToString(logType) << "', "
        << "'" << event << "', "
        << "'" << username << "')";
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_pDB->executeQuery(query.str());
}
// æ·»åŠ æ—¥å¿—ï¼ˆæŒ‡å®šç”¨æˆ·ï¼‰
bool SystemLogManager::log(LogType logType, const std::string& event, const std::string& username) {
    if (!m_pDB) {
        throw std::runtime_error("Database connection is not set.");
    }
    cleanOldLogs();
    std::ostringstream query;
    query << "INSERT INTO system_logs (log_type, event, username) VALUES ("
        << "'" << logTypeToString(logType) << "', "
        << "'" << event << "', "
        << "'" << username << "')";
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_pDB->executeQuery(query.str());
}
// èŽ·å–æ—¥å¿—å†…å®¹
std::vector<std::vector<std::string>> SystemLogManager::getLogs(int startPosition, int count) {
    if (!m_pDB) {
        throw std::runtime_error("Database connection is not set.");
    }
    std::ostringstream query;
    if (startPosition == -1 && count == -1) {
        query << "SELECT id, log_type, event, username, datetime(timestamp, 'localtime') FROM system_logs";
    }
    else {
        query << "SELECT id, log_type, event, username, datetime(timestamp, 'localtime') FROM system_logs "
            << "LIMIT " << count << " OFFSET " << startPosition;
    }
    return m_pDB->fetchResults(query.str());
}
// èŽ·å–ç­›é€‰åŽçš„æ—¥å¿—æ•°æ®
std::vector<std::vector<std::string>> SystemLogManager::getFilteredLogs(
    const std::string& logType,
    const std::string& username,
    const std::string& description,
    const std::string& startTime,
    const std::string& endTime,
    int pageNumber,
    int pageSize)
{
    if (!m_pDB) {
        throw std::runtime_error("Database connection is not set.");
    }
    std::ostringstream query;
    query << "SELECT id, log_type, event, username, datetime(timestamp, 'localtime') FROM system_logs WHERE 1=1";
    if (logType != "ALL") {
        query << " AND log_type = '" << logType << "'";
    }
    if (username != "ALL") {
        query << " AND username = '" << username << "'";
    }
    if (!description.empty()) {
        query << " AND event LIKE '%" << description << "%'";
    }
    if (!startTime.empty()) {
        query << " AND timestamp >= '" << startTime << "'";
    }
    if (!endTime.empty()) {
        query << " AND timestamp <= '" << endTime << "'";
    }
    int offset = (pageNumber - 1) * pageSize;
    query << " ORDER BY timestamp DESC LIMIT " << pageSize << " OFFSET " << offset;
    return m_pDB->fetchResults(query.str());
}
// èŽ·å–ç¬¦åˆæ¡ä»¶çš„æ—¥å¿—æ€»æ•°
int SystemLogManager::getTotalLogCount(
    const std::string& logType,
    const std::string& username,
    const std::string& description,
    const std::string& startTime,
    const std::string& endTime)
{
    if (!m_pDB) {
        throw std::runtime_error("Database connection is not set.");
    }
    std::ostringstream query;
    query << "SELECT COUNT(*) FROM system_logs WHERE 1=1";
    if (logType != "ALL") {
        query << " AND log_type = '" << logType << "'";
    }
    if (username != "ALL") {
        query << " AND username = '" << username << "'";
    }
    if (!description.empty()) {
        query << " AND event LIKE '%" << description << "%'";
    }
    if (!startTime.empty()) {
        query << " AND timestamp >= '" << startTime << "'";
    }
    if (!endTime.empty()) {
        query << " AND timestamp <= '" << endTime << "'";
    }
    auto results = m_pDB->fetchResults(query.str());
    return (!results.empty() && !results[0].empty()) ? std::stoi(results[0][0]) : 0;
}
// æ¸…理超过指定天数的旧日志
void SystemLogManager::cleanOldLogs(int daysToKeep) {
    if (!m_pDB) {
        throw std::runtime_error("Database connection is not set.");
    }
    std::ostringstream query;
    query << "DELETE FROM system_logs WHERE timestamp < datetime('now', '-" << daysToKeep << " days')";
    m_pDB->executeQuery(query.str());
}
// è½¬æ¢æ—¥å¿—类型为字符串
std::string SystemLogManager::logTypeToString(LogType logType) {
    switch (logType) {
    case LogType::Info:
        return "信息";
    case LogType::Error:
        return "错误";
    case LogType::Operation:
        return "操作";
    default:
        return "δ֪";
    }
}
SourceCode/Bond/Servo/SystemLogManager.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
#ifndef SYSTEM_LOG_MANAGER_H
#define SYSTEM_LOG_MANAGER_H
#include <string>
#include <vector>
#include <mutex>
#include "Database.h"
// ç³»ç»Ÿæ—¥å¿—管理类
class SystemLogManager {
public:
    // æ—¥å¿—类型定义
    enum class LogType {
        Info,
        Error,
        Operation,
        Unknown
    };
    // èŽ·å–å•ä¾‹å®žä¾‹
    static SystemLogManager& getInstance();
    // è®¾ç½®æ•°æ®åº“连接
    void setDatabase(BL::Database* db);
    // åˆå§‹åŒ–日志表
    bool initializeLogTable();
    // æ·»åŠ æ—¥å¿—
    bool log(LogType logType, const std::string& event);
    bool log(LogType logType, const std::string& event, const std::string& username);
    // èŽ·å–æ—¥å¿—å†…å®¹
    std::vector<std::vector<std::string>> getLogs(int startPosition = -1, int count = -1);
    // èŽ·å–ç­›é€‰åŽçš„æ—¥å¿—æ•°æ®
    std::vector<std::vector<std::string>> getFilteredLogs(
        const std::string& logType,
        const std::string& username,
        const std::string& description,
        const std::string& startTime,
        const std::string& endTime,
        int pageNumber,
        int pageSize);
    // èŽ·å–ç¬¦åˆæ¡ä»¶çš„æ—¥å¿—æ€»æ•°
    int getTotalLogCount(
        const std::string& logType,
        const std::string& username,
        const std::string& description,
        const std::string& startTime,
        const std::string& endTime);
    // æ¸…理超过指定天数的旧日志
    void cleanOldLogs(int daysToKeep = 30);
    // è½¬æ¢æ—¥å¿—类型为字符串
    static std::string logTypeToString(LogType logType);
private:
    // æž„造函数和析构函数
    SystemLogManager();
    ~SystemLogManager();
    // ç¦æ­¢æ‹·è´å’Œèµ‹å€¼
    SystemLogManager(const SystemLogManager&) = delete;
    SystemLogManager& operator=(const SystemLogManager&) = delete;
    // æ•°æ®åº“连接
    BL::Database* m_pDB = nullptr;
    // çº¿ç¨‹å®‰å…¨é”
    static std::mutex m_mutex;
};
#endif // SYSTEM_LOG_MANAGER_H
SourceCode/Bond/Servo/SystemLogManagerDlg.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,293 @@
// SystemLogManagerDlg.cpp: å®žçŽ°æ–‡ä»¶
//
#include "stdafx.h"
#include "Servo.h"
#include "afxdialogex.h"
#include "SystemLogManagerDlg.h"
#include "UserManager.h"
#include "SystemLogManager.h"
// CSystemLogManagerDlg å¯¹è¯æ¡†
IMPLEMENT_DYNAMIC(CSystemLogManagerDlg, CBaseDlg)
CSystemLogManagerDlg::CSystemLogManagerDlg(CWnd* pParent /*=nullptr*/)
    : CBaseDlg(IDD_DIALOG_SYSTEM_LOG_MANAGER, pParent)
{
}
CSystemLogManagerDlg::~CSystemLogManagerDlg()
{
}
void CSystemLogManagerDlg::DoDataExchange(CDataExchange* pDX)
{
    CBaseDlg::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_COMBO_TYPE, m_comboType);
    DDX_Control(pDX, IDC_COMBO_USER, m_comboUser);
    DDX_Control(pDX, IDC_DATETIMEPICKER_START, m_dateTimeStart);
    DDX_Control(pDX, IDC_DATETIMEPICKER_END, m_dateTimeEnd);
    DDX_Control(pDX, IDC_EDIT_DESCRIPTION, m_editDescription);
    DDX_Control(pDX, IDC_CUSTOM_LIST_LOGS, m_listLogs);
    DDX_Control(pDX, IDC_STATIC_PAGE_NUMBER, m_staticPageNum);
}
void CSystemLogManagerDlg::InitSystemLogManager()
{
    if (m_listLogs.GetSafeHwnd() == NULL)
        return;
    int nRows = 21; // åŒ…括表头(1 è¡Œï¼‰å’Œæ•°æ®ï¼ˆ20 è¡Œï¼‰
    int nCols = 5;
    int nFixRows = 1;
    int nFixCols = 0;
    int nRowIdx = 0;
    int nColIdx = 0;
    m_listLogs.DeleteAllItems();
    m_listLogs.SetVirtualMode(FALSE);
    m_listLogs.GetDefaultCell(TRUE, FALSE)->SetBackClr(g_nGridFixCellColor); // è®¾ç½®å›ºå®šè¡ŒèƒŒæ™¯è‰²
    m_listLogs.GetDefaultCell(FALSE, TRUE)->SetBackClr(g_nGridFixCellColor); // è®¾ç½®å›ºå®šåˆ—背景色
    m_listLogs.GetDefaultCell(FALSE, FALSE)->SetBackClr(g_nGridCellColor);    // è®¾ç½®å•元格背景色
    m_listLogs.SetFixedTextColor(g_nGridFixFontColor); // è®¾ç½®å›ºå®šè¡Œåˆ—字体颜色
    m_listLogs.SetRowCount(nRows);
    m_listLogs.SetColumnCount(nCols);
    m_listLogs.SetFixedRowCount(nFixRows);
    m_listLogs.SetFixedColumnCount(nFixCols);
    // Col
    m_listLogs.SetColumnWidth(nColIdx, 10);
    m_listLogs.SetItemText(nRowIdx, nColIdx++, _T("No."));
    m_listLogs.SetColumnWidth(nColIdx, 10);
    m_listLogs.SetItemText(nRowIdx, nColIdx++, _T("类型"));
    m_listLogs.SetColumnWidth(nColIdx, 100);
    m_listLogs.SetItemText(nRowIdx, nColIdx++, _T("事件"));
    m_listLogs.SetColumnWidth(nColIdx, 30);
    m_listLogs.SetItemText(nRowIdx, nColIdx++, _T("用户"));
    m_listLogs.SetColumnWidth(nColIdx, 50);
    m_listLogs.SetItemText(nRowIdx, nColIdx++, _T("时间"));
    // åˆ›å»º 20 è¡Œç©ºç™½æ•°æ®è¡Œ
    for (int i = 1; i < nRows; ++i) {
        for (int j = 0; j < nCols; ++j) {
            m_listLogs.SetItemText(i, j, _T("")); // åˆå§‹åŒ–为空字符串
        }
    }
    m_listLogs.SetFixedRowSelection(FALSE);
    m_listLogs.SetFixedColumnSelection(FALSE);
    m_listLogs.SetEditable(FALSE);
    m_listLogs.SetRowResize(FALSE);
    m_listLogs.SetColumnResize(TRUE);
    m_listLogs.ExpandColumnsToFit(TRUE);
    m_listLogs.SetListMode(TRUE);                // å¯ç”¨åˆ—表模式
    m_listLogs.EnableSelection(TRUE);            // å¯ç”¨é€‰æ‹©
    m_listLogs.SetSingleRowSelection(TRUE);        // è‡ªåŠ¨æ•´è¡Œé«˜äº®ï¼ˆé™åˆ¶ä¸ºå•è¡Œé€‰æ‹©ï¼‰
    m_listLogs.ExpandLastColumn();                // æœ€åŽä¸€åˆ—填充网格
    try {
        FillSystemLogManager();
    }
    catch (const std::exception& ex) {
        CString errorMsg;
        errorMsg.Format(_T("初始化运行日志失败:%s"), CString(ex.what()));
        AfxMessageBox(errorMsg, MB_ICONERROR);
    }
}
void CSystemLogManagerDlg::FillSystemLogManager()
{
    // èŽ·å–ç­›é€‰æ¡ä»¶
    CString selectedType, selectedUser, description;
    m_comboType.GetLBText(m_comboType.GetCurSel(), selectedType);
    m_comboUser.GetLBText(m_comboUser.GetCurSel(), selectedUser);
    m_editDescription.GetWindowText(description);
    COleDateTime startTime, endTime;
    m_dateTimeStart.GetTime(startTime);
    m_dateTimeEnd.GetTime(endTime);
    CString strStartTime = startTime.Format(_T("%Y-%m-%d %H:%M:%S"));
    CString strEndTime = endTime.Format(_T("%Y-%m-%d %H:%M:%S"));
    std::string type = CT2A(selectedType);
    std::string user = CT2A(selectedUser);
    std::string desc = CT2A(description);
    std::string start = CT2A(strStartTime);
    std::string end = CT2A(strEndTime);
    // èŽ·å–æ—¥å¿—ç®¡ç†å®žä¾‹
    SystemLogManager& logManager = SystemLogManager::getInstance();
    int pageSize = 20; // æ¯é¡µæ˜¾ç¤º 20 æ¡è®°å½•
    int totalRecords = logManager.getTotalLogCount(type, user, desc, start, end);
    m_nTotalPages = (totalRecords + pageSize - 1) / pageSize;
    auto logs = logManager.getFilteredLogs(type, user, desc, start, end, m_nCurrentPage, pageSize);
    // æ›´æ–°è¡¨æ ¼æ•°æ®
    int rowIdx = 1;
    for (const auto& log : logs) {
        m_listLogs.SetItemText(rowIdx, 0, CString(std::to_string(rowIdx).c_str())); // åºå·
        m_listLogs.SetItemText(rowIdx, 1, CString(log[1].c_str()));                 // ç±»åž‹
        m_listLogs.SetItemText(rowIdx, 2, CString(log[2].c_str()));                 // äº‹ä»¶
        m_listLogs.SetItemText(rowIdx, 3, CString(log[3].c_str()));                 // ç”¨æˆ·
        m_listLogs.SetItemText(rowIdx, 4, CString(log[4].c_str()));                 // æ—¶é—´
        ++rowIdx;
    }
    // æ¸…空多余行
    for (; rowIdx <= 20; ++rowIdx) {
        m_listLogs.SetItemText(rowIdx, 0, CString(std::to_string(rowIdx).c_str())); // åºå·åˆ—
        for (int colIdx = 1; colIdx < m_listLogs.GetColumnCount(); ++colIdx) {
            m_listLogs.SetItemText(rowIdx, colIdx, _T(""));
        }
    }
    m_listLogs.ExpandColumnsToFit(FALSE);
    m_listLogs.ExpandLastColumn();
    m_listLogs.Invalidate();
    m_listLogs.UpdateWindow();
    UpdatePageInfo();
}
void CSystemLogManagerDlg::UpdatePageInfo()
{
    // æ ¼å¼åŒ–页码信息为 "当前页/总页数"
    CString pageInfo;
    pageInfo.Format(_T("%d/%d é¡µ"), m_nCurrentPage, m_nTotalPages);
    m_staticPageNum.SetWindowText(pageInfo);
}
BEGIN_MESSAGE_MAP(CSystemLogManagerDlg, CBaseDlg)
    ON_BN_CLICKED(IDC_BUTTON_SEARCH, &CSystemLogManagerDlg::OnBnClickedButtonSearch)
    ON_BN_CLICKED(IDC_BUTTON_PREV_PAGE, &CSystemLogManagerDlg::OnBnClickedButtonPrevPage)
    ON_BN_CLICKED(IDC_BUTTON_NEXT_PAGE, &CSystemLogManagerDlg::OnBnClickedButtonNextPage)
    ON_CBN_SELCHANGE(IDC_COMBO_TYPE, &CSystemLogManagerDlg::OnSelchangeComboType)
    ON_CBN_SELCHANGE(IDC_COMBO_USER, &CSystemLogManagerDlg::OnSelchangeComboUser)
END_MESSAGE_MAP()
// CSystemLogManagerDlg æ¶ˆæ¯å¤„理程序
BOOL CSystemLogManagerDlg::OnInitDialog()
{
    CBaseDlg::OnInitDialog();
    // TODO:  åœ¨æ­¤æ·»åŠ é¢å¤–çš„åˆå§‹åŒ–
    SetWindowText(_T("系统运行日志"));
    m_nCurrentPage = 1;  // ä»Žç¬¬ä¸€é¡µå¼€å§‹
    m_nTotalPages = 1;   // é»˜è®¤æ€»é¡µæ•°ä¸º 1
    m_comboType.AddString(_T("ALL"));
    m_comboType.AddString(_T("信息"));
    m_comboType.AddString(_T("操作"));
    m_comboType.AddString(_T("错误"));
    m_comboType.AddString(_T("未知"));
    m_comboType.SetCurSel(0);
    m_comboUser.AddString(_T("ALL"));
    m_comboUser.AddString(_T("SYSTEM"));
    auto usernames = UserManager::getInstance().getUsernames();
    for (const auto& username : usernames) {
        CString cstrUsername(username.c_str());
        m_comboUser.AddString(cstrUsername);
    }
    m_comboUser.SetCurSel(0);
    // è®¾ç½®ä¸º 30 å¤©å‰
    COleDateTime currentTime = COleDateTime::GetCurrentTime();
    COleDateTime defaultStartTime = currentTime - COleDateTimeSpan(30, 0, 0, 0);
    m_dateTimeStart.SetTime(defaultStartTime);
    InitSystemLogManager();
    return TRUE;  // return TRUE unless you set the focus to a control
    // å¼‚常: OCX å±žæ€§é¡µåº”返回 FALSE
}
void CSystemLogManagerDlg::OnBnClickedButtonSearch()
{
    // TODO: åœ¨æ­¤æ·»åŠ æŽ§ä»¶é€šçŸ¥å¤„ç†ç¨‹åºä»£ç 
    try {
        m_nCurrentPage = 1;
        FillSystemLogManager();
    }
    catch (const std::exception& ex) {
        CString errorMsg;
        errorMsg.Format(_T("搜索失败:%s"), CString(ex.what()));
        AfxMessageBox(errorMsg, MB_ICONERROR);
    }
}
void CSystemLogManagerDlg::OnBnClickedButtonPrevPage()
{
    // TODO: åœ¨æ­¤æ·»åŠ æŽ§ä»¶é€šçŸ¥å¤„ç†ç¨‹åºä»£ç 
    try {
        if (m_nCurrentPage > 1) {
            m_nCurrentPage--;
            FillSystemLogManager();
        }
        else {
            AfxMessageBox(_T("已经是第一页!"));
        }
    }
    catch (const std::exception& ex) {
        CString errorMsg;
        errorMsg.Format(_T("切换到上一页失败:%s"), CString(ex.what()));
        AfxMessageBox(errorMsg, MB_ICONERROR);
    }
}
void CSystemLogManagerDlg::OnBnClickedButtonNextPage()
{
    // TODO: åœ¨æ­¤æ·»åŠ æŽ§ä»¶é€šçŸ¥å¤„ç†ç¨‹åºä»£ç 
    try {
        if (m_nCurrentPage < m_nTotalPages) {
            m_nCurrentPage++;
            FillSystemLogManager();
        }
        else {
            AfxMessageBox(_T("已经是最后一页!"));
        }
    }
    catch (const std::exception& ex) {
        CString errorMsg;
        errorMsg.Format(_T("切换到下一页失败:%s"), CString(ex.what()));
        AfxMessageBox(errorMsg, MB_ICONERROR);
    }
}
void CSystemLogManagerDlg::OnSelchangeComboType()
{
    // TODO: åœ¨æ­¤æ·»åŠ æŽ§ä»¶é€šçŸ¥å¤„ç†ç¨‹åºä»£ç 
    try {
        m_nCurrentPage = 1;
        FillSystemLogManager();
    }
    catch (const std::exception& ex) {
        CString errorMsg;
        errorMsg.Format(_T("切换类型失败:%s"), CString(ex.what()));
        AfxMessageBox(errorMsg, MB_ICONERROR);
    }
}
void CSystemLogManagerDlg::OnSelchangeComboUser()
{
    // TODO: åœ¨æ­¤æ·»åŠ æŽ§ä»¶é€šçŸ¥å¤„ç†ç¨‹åºä»£ç 
    try {
        m_nCurrentPage = 1;
        FillSystemLogManager();
    }
    catch (const std::exception& ex) {
        CString errorMsg;
        errorMsg.Format(_T("切换角色失败:%s"), CString(ex.what()));
        AfxMessageBox(errorMsg, MB_ICONERROR);
    }
}
SourceCode/Bond/Servo/SystemLogManagerDlg.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
#pragma once
#include "afxdialogex.h"
#include "GridCtrl.h"
#include "CBaseDlg.h"
// CSystemLogManagerDlg å¯¹è¯æ¡†
class CSystemLogManagerDlg : public CBaseDlg
{
    DECLARE_DYNAMIC(CSystemLogManagerDlg)
public:
    CSystemLogManagerDlg(CWnd* pParent = nullptr);   // æ ‡å‡†æž„造函数
    virtual ~CSystemLogManagerDlg();
// å¯¹è¯æ¡†æ•°æ®
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_DIALOG_SYSTEM_LOG_MANAGER };
#endif
private:
    void InitSystemLogManager();
    void FillSystemLogManager();
    void UpdatePageInfo();
private:
    int m_nCurrentPage;    // å½“前页码
    int m_nTotalPages;     // æ€»é¡µæ•°
private:
    CComboBox m_comboType;
    CComboBox m_comboUser;
    CDateTimeCtrl m_dateTimeStart;
    CDateTimeCtrl m_dateTimeEnd;
    CEdit m_editDescription;
    CGridCtrl m_listLogs;
    CStatic m_staticPageNum;
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV æ”¯æŒ
    virtual BOOL OnInitDialog();
    afx_msg void OnBnClickedButtonSearch();
    afx_msg void OnBnClickedButtonPrevPage();
    afx_msg void OnBnClickedButtonNextPage();
    afx_msg void OnSelchangeComboType();
    afx_msg void OnSelchangeComboUser();
    DECLARE_MESSAGE_MAP()
};
SourceCode/Bond/Servo/UserManager.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,542 @@
#include "stdafx.h"
#include "UserManager.h"
#include <chrono>
#include <iostream>
#include <fstream>
#include <ctime>
#include <sstream>
const std::string SESSION_FILE = R"(session.dat)";
const std::string DATABASE_FILE = R"(BondEq.db)";
const std::string INITIAL_ADMIN_USERNAME = "admin";
const std::string INITIAL_ADMIN_PASSWORD = "admin";
// èŽ·å–å•ä¾‹å®žä¾‹
UserManager& UserManager::getInstance() {
    static UserManager instance;
    return instance;
}
UserManager::UserManager()
    : m_isLoggedIn(false), m_isRememberMe(false), m_tmSessionTimeout(std::chrono::minutes(30)),
    m_tmSessionExpiration(std::chrono::hours(72)), m_hMouseHook(nullptr), m_hKeyboardHook(nullptr),
    m_pDB(std::make_unique<BL::SQLiteDatabase>()) {
    initializeDatabase();
}
UserManager::~UserManager() {
    terminateIdleDetection();
}
// æä¾›æ•°æ®åº“连接
std::unique_ptr<BL::Database>& UserManager::getDatabaseInstance() {
    return m_pDB;
}
// åˆå§‹åŒ–数据库,创建用户表并插入初始管理员用户
bool UserManager::initializeDatabase() {
    std::string dbFilePath = getDatabaseFilePath();
    if (!m_pDB->connect(dbFilePath, true)) {
        throw std::runtime_error("Failed to connect to database.");
    }
    std::string createTableQuery = R"(
        CREATE TABLE IF NOT EXISTS users (
            username VARCHAR(50) PRIMARY KEY,
            password VARCHAR(255) NOT NULL,
            role INT NOT NULL,
            session_timeout INT DEFAULT 30,
            session_expiration INT DEFAULT 72,
            last_login DATETIME DEFAULT (datetime('now', 'localtime'))
        )
    )";
    m_pDB->executeQuery(createTableQuery);
    std::string checkAdminQuery = "SELECT COUNT(*) FROM users WHERE role = 0";
    auto result = m_pDB->fetchResults(checkAdminQuery);
    if (result.empty() || result[0][0] == "0") {
        std::string insertAdminQuery = "INSERT INTO users (username, password, role, session_timeout, session_expiration) VALUES ('" +
            INITIAL_ADMIN_USERNAME + "', '" + simpleEncryptDecrypt(INITIAL_ADMIN_PASSWORD, "BandKey") + "', 0, 30, 72)";
        m_pDB->executeQuery(insertAdminQuery);
    }
    return true;
}
// å¯¹å¯†ç è¿›è¡Œå“ˆå¸Œå¤„理
std::string UserManager::hashPassword(const std::string& password) {
    return std::to_string(std::hash<std::string>{}(password));
}
// ç®€å•的加密和解密函数
std::string UserManager::simpleEncryptDecrypt(const std::string& data, const std::string& key) {
    std::string result = data;
    for (size_t i = 0; i < data.size(); ++i) {
        result[i] ^= key[i % key.size()];  // ç®€å•异或加密
    }
    return result;
}
// ä»Žä¼šè¯æ–‡ä»¶åŠ è½½ä¼šè¯ä¿¡æ¯
bool UserManager::loadSession() {
    std::ifstream sessionFile(getSessionFilePath(), std::ios::binary);
    if (!sessionFile.is_open()) {
        return false;
    }
    // ä»Žæ–‡ä»¶è¯»å–加密数据
    std::string encryptedData((std::istreambuf_iterator<char>(sessionFile)), std::istreambuf_iterator<char>());
    sessionFile.close();
    // è§£å¯†æ•°æ®
    std::string decryptedData = simpleEncryptDecrypt(encryptedData, "my_secret_key");
    // è§£æžè§£å¯†çš„æ•°æ®
    std::istringstream sessionData(decryptedData);
    std::string username;
    std::string password;
    std::time_t lastLoginTime;
    int timeoutMinutes;
    int expirationHours;
    sessionData >> username >> password >> lastLoginTime >> timeoutMinutes >> expirationHours;
    // éªŒè¯æ—¶é—´æˆ³æœ‰æ•ˆæ€§
    auto now = std::chrono::system_clock::now();
    auto lastLogin = std::chrono::system_clock::from_time_t(lastLoginTime);
    auto sessionDuration = std::chrono::duration_cast<std::chrono::hours>(now - lastLogin);
    if (sessionDuration > std::chrono::hours(expirationHours)) {
        clearSession();
        return false;
    }
    // æ¢å¤ä¼šè¯æ•°æ®
    m_strCurrentUser = username;
    m_strCurrentPass = password;
    m_tpLastLogin = lastLogin;
    m_tmSessionTimeout = std::chrono::minutes(timeoutMinutes);
    m_tmSessionExpiration = std::chrono::hours(expirationHours);
    m_isLoggedIn = true;
    m_isRememberMe = true;
    updateActivityTime();
    return true;
}
// ä¿å­˜ä¼šè¯ä¿¡æ¯åˆ°æ–‡ä»¶
void UserManager::saveSession() {
    if (!m_isRememberMe) {
        clearSession();
        return;
    }
    // åŽŸå§‹ä¼šè¯æ•°æ®
    std::stringstream sessionData;
    std::time_t lastLoginTime = std::chrono::system_clock::to_time_t(m_tpLastLogin);
    sessionData << m_strCurrentUser << " " << m_strCurrentPass << " " << lastLoginTime << " "
        << m_tmSessionTimeout.count() << " " << m_tmSessionExpiration.count();
    // åŠ å¯†æ•°æ®
    std::string encryptedData = simpleEncryptDecrypt(sessionData.str(), "my_secret_key");
    // å†™å…¥åŠ å¯†æ•°æ®åˆ°æ–‡ä»¶
    std::ofstream sessionFile(getSessionFilePath(), std::ios::binary);
    if (sessionFile.is_open()) {
        sessionFile << encryptedData;
        sessionFile.close();
    }
}
// æ¸…除会话文件
void UserManager::clearSession() {
    std::remove(getSessionFilePath().c_str());
}
// èŽ·å–ç¨‹åºè·¯å¾„ä¸‹çš„config文件夹路径
std::string UserManager::getConfigFolderPath() {
    char path[MAX_PATH];
    GetModuleFileName(NULL, path, MAX_PATH);
    std::string exePath = std::string(path).substr(0, std::string(path).find_last_of("\\/"));
    std::string configPath = exePath + "\\Config\\";
    // æ£€æŸ¥å¹¶åˆ›å»ºconfig文件夹
    DWORD fileAttr = GetFileAttributes(configPath.c_str());
    if (fileAttr == INVALID_FILE_ATTRIBUTES) {
        CreateDirectory(configPath.c_str(), NULL);
    }
    return configPath;
}
// èŽ·å–session.dat文件路径
std::string UserManager::getSessionFilePath() {
    return getConfigFolderPath() + SESSION_FILE;
}
// èŽ·å–æ•°æ®åº“æ–‡ä»¶è·¯å¾„
std::string UserManager::getDatabaseFilePath() {
    return getConfigFolderPath() + DATABASE_FILE;
}
// ç™»å½•方法
bool UserManager::login(const std::string& username, const std::string& password, bool rememberMeFlag) {
    std::string query = "SELECT username, password, role, session_timeout, session_expiration FROM users WHERE username = '" + username + "'";
    auto result = m_pDB->fetchResults(query);
    if (result.empty() || result[0][1] != simpleEncryptDecrypt(password, "BandKey")) {
        std::cerr << "Login failed: Invalid username or password." << std::endl;
        return false;
    }
    m_strCurrentUser = username;
    m_strCurrentPass = password;
    m_enCurrentUserRole = static_cast<UserRole>(std::stoi(result[0][2]));
    m_tmSessionTimeout = std::chrono::minutes(std::stoi(result[0][3]));
    m_tmSessionExpiration = std::chrono::hours(std::stoi(result[0][4]));
    m_isLoggedIn = true;
    m_isRememberMe = rememberMeFlag;
    updateActivityTime();
    m_tpLastLogin = std::chrono::system_clock::now();
    std::string updateLoginTime = "UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE username = '" + username + "'";
    m_pDB->executeQuery(updateLoginTime);
    saveSession();
    return true;
}
// ç™»å‡ºæ–¹æ³•
void UserManager::logout() {
    if (m_isLoggedIn) {
        std::cout << "User logged out: " << m_strCurrentUser << std::endl;
        m_strCurrentUser.clear();
        m_strCurrentPass.clear();
        m_isLoggedIn = false;
        m_isRememberMe = false;
        clearSession();
    }
}
// è¿”回当前用户的登录状态
bool UserManager::isLoggedIn() const {
    return m_isLoggedIn;
}
// è¿”回当前用户的记住登录状态
bool UserManager::isRememberMe() const {
    return m_isRememberMe;
}
// åˆ›å»ºæ–°ç”¨æˆ·ï¼Œä»…超级管理员有权限
bool UserManager::createUser(const std::string& username, const std::string& password, UserRole role,
    std::chrono::minutes timeout, std::chrono::hours expiration) {
    if (m_enCurrentUserRole != UserRole::SuperAdmin) {
        std::cerr << "Only SuperAdmin can create new users." << std::endl;
        return false;
    }
    std::string query = "INSERT INTO users (username, password, role, session_timeout, session_expiration) VALUES ('" +
        username + "', '" + simpleEncryptDecrypt(password, "BandKey") + "', " + std::to_string(static_cast<int>(role)) + ", " +
        std::to_string(timeout.count()) + ", " + std::to_string(expiration.count()) + ")";
    return m_pDB->executeQuery(query);
}
// åˆ é™¤ç”¨æˆ·ï¼Œä»…超级管理员有权限,且不能删除自己
bool UserManager::deleteUser(const std::string& username) {
    if (m_enCurrentUserRole != UserRole::SuperAdmin) {
        std::cerr << "Only SuperAdmin can delete users." << std::endl;
        return false;
    }
    if (username == m_strCurrentUser) {
        std::cerr << "SuperAdmin cannot delete their own account." << std::endl;
        return false;
    }
    std::string query = "DELETE FROM users WHERE username = '" + username + "'";
    return m_pDB->executeQuery(query);
}
// èŽ·å–æ‰€æœ‰ç”¨æˆ·ä¿¡æ¯ï¼Œä»…è¶…çº§ç®¡ç†å‘˜æœ‰æƒé™
std::vector<std::vector<std::string>> UserManager::getUsers() {
    if (m_enCurrentUserRole != UserRole::SuperAdmin) {
        std::cerr << "Only SuperAdmin can retrieve user data." << std::endl;
        return {};
    }
    // æŸ¥è¯¢æ•´ä¸ªç”¨æˆ·è¡¨
    std::string query = "SELECT username, password, role, session_timeout, session_expiration, last_login FROM users";
    std::vector<std::vector<std::string>> results = m_pDB->fetchResults(query);
    for (auto& row : results) {
        row[1] = simpleEncryptDecrypt(row[1], "BandKey");
    }
    return results;
}
// è®¾ç½®æ•´ä¸ªç”¨æˆ·è¡¨çš„æ•°æ®ï¼Œä»…超级管理员有权限
bool UserManager::setUsers(const std::vector<std::vector<std::string>>& usersData) {
    if (m_enCurrentUserRole != UserRole::SuperAdmin) {
        std::cerr << "Only SuperAdmin can set user data." << std::endl;
        return false;
    }
    // æ¸…空用户表
    std::string deleteQuery = "DELETE FROM users";
    if (!m_pDB->executeQuery(deleteQuery)) {
        std::cerr << "Failed to clear the users table." << std::endl;
        return false;
    }
    // æ’入新的用户数据
    for (const auto& user : usersData) {
        if (user.size() != 6) {
            std::cerr << "Invalid data format for user. Each user must have 6 fields." << std::endl;
            return false;
        }
        std::string insertQuery = "INSERT INTO users (username, password, role, session_timeout, session_expiration, last_login) VALUES ('" +
            user[0] + "', '" + simpleEncryptDecrypt(user[1], "BandKey") + "', " + user[2] + ", " + user[3] + ", " + user[4] + ", '" + user[5] + "')";
        if (!m_pDB->executeQuery(insertQuery)) {
            std::cerr << "Failed to insert user: " << user[0] << std::endl;
            return false;
        }
    }
    return true;
}
// ä¿®æ”¹ç”¨æˆ·åï¼Œä»…超级管理员有权限
bool UserManager::changeUsername(const std::string& username, const std::string& newUsername) {
    if (m_enCurrentUserRole != UserRole::SuperAdmin) {
        std::cerr << "Only SuperAdmin can change usernames." << std::endl;
        return false;
    }
    std::string query = "UPDATE users SET username = '" + newUsername + "' WHERE username = '" + username + "'";
    bool success = m_pDB->executeQuery(query);
    // å¦‚果是当前登录用户修改自己的用户名,更新成员变量并保存会话文件
    if (success && m_strCurrentUser == username) {
        m_strCurrentUser = newUsername;
        // å¦‚果“记住密码”已启用,更新会话文件
        if (m_isRememberMe) {
            saveSession();
        }
    }
    return success;
}
// ä¿®æ”¹ç”¨æˆ·å¯†ç ï¼ˆä»…允许当前用户或超级管理员)
bool UserManager::changePassword(const std::string& username, const std::string& newPassword) {
    if (username != m_strCurrentUser && m_enCurrentUserRole != UserRole::SuperAdmin) {
        std::cerr << "Permission denied: Only the user or SuperAdmin can change passwords." << std::endl;
        return false;
    }
    std::string query = "UPDATE users SET password = '" + simpleEncryptDecrypt(newPassword, "BandKey") +
        "' WHERE username = '" + username + "'";
    bool success = m_pDB->executeQuery(query);
    // å¦‚果是当前用户修改自己的密码,退出登录并清除会话文件
    if (success && m_strCurrentUser == username) {
        logout();
        std::cout << "Password changed successfully. Please log in again." << std::endl;
    }
    return success;
}
// æ›´æ”¹ç”¨æˆ·è§’色,仅超级管理员有权限
bool UserManager::changeUserRole(const std::string& username, UserRole newRole) {
    if (m_enCurrentUserRole != UserRole::SuperAdmin) {
        std::cerr << "Only SuperAdmin can change user roles." << std::endl;
        return false;
    }
    // é˜²æ­¢ç®¡ç†å‘˜æ›´æ”¹è‡ªå·±çš„角色
    if (m_strCurrentUser == username) {
        std::cerr << "SuperAdmin cannot change their own role." << std::endl;
        return false;
    }
    std::string query = "UPDATE users SET role = " + std::to_string(static_cast<int>(newRole)) +
        " WHERE username = '" + username + "'";
    return m_pDB->executeQuery(query);
}
// ä¿®æ”¹ç”¨æˆ·çš„ session_timeout,仅超级管理员有权限
bool UserManager::changeUserSessionTimeout(const std::string& username, int newTimeoutMinutes) {
    if (m_enCurrentUserRole != UserRole::SuperAdmin) {
        std::cerr << "Only SuperAdmin can change session timeout." << std::endl;
        return false;
    }
    std::string query = "UPDATE users SET session_timeout = " + std::to_string(newTimeoutMinutes) +
        " WHERE username = '" + username + "'";
    bool success = m_pDB->executeQuery(query);
    // å¦‚果是当前登录用户修改自己的超时设置,更新成员变量
    if (success && m_strCurrentUser == username) {
        m_tmSessionTimeout = std::chrono::minutes(newTimeoutMinutes);
        if (m_isRememberMe) {
            saveSession();
        }
    }
    return success;
}
// ä¿®æ”¹ç”¨æˆ·çš„ session_expiration,仅超级管理员有权限
bool UserManager::changeUserSessionExpiration(const std::string& username, int newExpirationHours) {
    if (m_enCurrentUserRole != UserRole::SuperAdmin) {
        std::cerr << "Only SuperAdmin can change session expiration." << std::endl;
        return false;
    }
    std::string query = "UPDATE users SET session_expiration = " + std::to_string(newExpirationHours) +
        " WHERE username = '" + username + "'";
    bool success = m_pDB->executeQuery(query);
    // å¦‚果是当前登录用户修改自己的过期设置,更新成员变量
    if (success && m_strCurrentUser == username) {
        m_tmSessionExpiration = std::chrono::hours(newExpirationHours);
        if (m_isRememberMe) {
            saveSession();
        }
    }
    return success;
}
// èŽ·å–æ‰€æœ‰ç”¨æˆ·åç§°
std::vector<std::string> UserManager::getUsernames() {
    std::vector<std::string> usernames;
    std::string query = "SELECT username FROM users";
    auto results = m_pDB->fetchResults(query);
    for (const auto& row : results) {
        if (!row.empty()) {
            usernames.push_back(row[0]); // èŽ·å–ç”¨æˆ·ååˆ—çš„å€¼
        }
    }
    return usernames;
}
// èŽ·å–æŒ‡å®šç”¨æˆ·åçš„ç”¨æˆ·ä¿¡æ¯
std::vector<std::string> UserManager::getUserInfo(const std::string& username)
{
    // æž„建查询语句
    std::ostringstream query;
    query << "SELECT username, password, role, session_timeout, session_expiration, last_login "
        << "FROM users WHERE username = '" << username << "'";
    // æ‰§è¡ŒæŸ¥è¯¢å¹¶èŽ·å–ç»“æžœ
    auto results = m_pDB->fetchResults(query.str());
    if (results.empty()) {
        return {};
    }
    // è¿”回查询到的第一行数据
    return results[0];
}
// æ›´æ–°æœ€åŽæ´»åŠ¨æ—¶é—´ï¼Œç”¨äºŽæ— æ“ä½œè¶…æ—¶æ£€æµ‹
void UserManager::updateActivityTime() {
    m_tpLastActivity = std::chrono::system_clock::now();
    std::cout << "Activity updated at: " << std::chrono::system_clock::to_time_t(m_tpLastActivity) << std::endl;
}
// è®¾ç½®æ— æ“ä½œè¶…æ—¶æ—¶é—´
void UserManager::setSessionTimeout(std::chrono::minutes timeout) {
    m_tmSessionTimeout = timeout;
}
// æ£€æŸ¥æ˜¯å¦è¶…过无操作超时时间
bool UserManager::isInactiveTimeout() const {
    auto now = std::chrono::system_clock::now();
    auto elapsedSeconds = std::chrono::duration_cast<std::chrono::seconds>(now - m_tpLastActivity).count();
    return elapsedSeconds > m_tmSessionTimeout.count() * 60;
}
// åˆå§‹åŒ–无操作检测,包括设置全局鼠标和键盘钩子
void UserManager::initializeIdleDetection(HWND hwnd) {
    updateActivityTime();
    m_hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, LowLevelMouseProc, (HINSTANCE) nullptr, 0);
    m_hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, (HINSTANCE) nullptr, 0);
    ::SetTimer(hwnd, 1, 60000, nullptr);
}
// ç»ˆæ­¢æ— æ“ä½œæ£€æµ‹ï¼Œæ¸…除鼠标和键盘钩子
void UserManager::terminateIdleDetection() {
    if (m_hMouseHook) {
        UnhookWindowsHookEx(m_hMouseHook);
        m_hMouseHook = nullptr;
    }
    if (m_hKeyboardHook) {
        UnhookWindowsHookEx(m_hKeyboardHook);
        m_hKeyboardHook = nullptr;
    }
    ::KillTimer(nullptr, 1);
}
// èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·å
std::string UserManager::getCurrentUser() const {
    return m_strCurrentUser;
}
// ä¿®æ”¹å½“前登录用户名
void UserManager::setCurrentUser(const std::string& strName) {
    m_strCurrentUser = strName;
}
// èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·å¯†ç 
std::string UserManager::getCurrentPass() const {
    return m_strCurrentPass;
}
// ä¿®æ”¹å½“前登录用户密码
void UserManager::setCurrentPass(const std::string& strPass) {
    m_strCurrentPass = strPass;
}
// èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·è§’è‰²
UserRole UserManager::getCurrentUserRole() const {
    return m_enCurrentUserRole;
}
// ä¿®æ”¹å½“前登录用户角色
void UserManager::setCurrentUserRole(UserRole emRole) {
    m_enCurrentUserRole = emRole;
}
// èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·çš„æ— æ“ä½œè¶…æ—¶æ—¶é—´
std::chrono::minutes UserManager::getSessionTimeout() const {
    return m_tmSessionTimeout;
}
// èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·çš„ä¼šè¯è¿‡æœŸæ—¶é—´
std::chrono::hours UserManager::getSessionExpiration() const {
    return m_tmSessionExpiration;
}
// å…¨å±€é¼ æ ‡é’©å­å›žè°ƒï¼Œè®°å½•活动时间
LRESULT CALLBACK UserManager::LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode == HC_ACTION) {
        UserManager::getInstance().updateActivityTime();
        std::cout << "Mouse event detected. Activity time updated." << std::endl;
    }
    return CallNextHookEx(nullptr, nCode, wParam, lParam);
}
// å…¨å±€é”®ç›˜é’©å­å›žè°ƒï¼Œè®°å½•活动时间
LRESULT CALLBACK UserManager::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode == HC_ACTION) {
        UserManager::getInstance().updateActivityTime();
        std::cout << "Keyboard event detected. Activity time updated." << std::endl;
    }
    return CallNextHookEx(nullptr, nCode, wParam, lParam);
}
SourceCode/Bond/Servo/UserManager.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,130 @@
#ifndef USER_MANAGER_H
#define USER_MANAGER_H
#include <string>
#include <memory>
#include <chrono>
#include <windows.h>
#include "Database.h"
// ç”¨æˆ·è§’色定义
enum class UserRole {
    SuperAdmin = 0,     // è¶…级管理员
    Engineer,           // å·¥ç¨‹å¸ˆ
    Operator            // æ“ä½œå‘˜
};
// ç”¨æˆ·ç®¡ç†ç±»ï¼Œé‡‡ç”¨å•例模式
class UserManager {
public:
    static UserManager& getInstance();
    UserManager(const UserManager&) = delete;
    UserManager& operator=(const UserManager&) = delete;
    // æä¾›æ•°æ®åº“连接
    std::unique_ptr<BL::Database>& getDatabaseInstance();
    // ç”¨æˆ·æ“ä½œ
    bool login(const std::string& username, const std::string& password, bool rememberMe = false);
    void logout();
    bool isLoggedIn() const;
    bool isRememberMe() const;
    bool createUser(const std::string& username, const std::string& password, UserRole role,
        std::chrono::minutes timeout = std::chrono::minutes(30),
        std::chrono::hours expiration = std::chrono::hours(72));
    bool deleteUser(const std::string& username);
    std::vector<std::vector<std::string>> getUsers();
    bool setUsers(const std::vector<std::vector<std::string>>& usersData);
    bool changeUsername(const std::string& username, const std::string& newUsername);
    bool changePassword(const std::string& username, const std::string& newPassword);
    bool changeUserRole(const std::string& username, UserRole newRole);
    bool changeUserSessionTimeout(const std::string& username, int newTimeoutMinutes);
    bool changeUserSessionExpiration(const std::string& username, int newExpirationHours);
    std::vector<std::string> getUsernames();
    std::vector<std::string> getUserInfo(const std::string& username);
    // ä¼šè¯æ–‡ä»¶æ“ä½œ
    bool loadSession();     // ä»Žä¼šè¯æ–‡ä»¶åŠ è½½ä¼šè¯ä¿¡æ¯
    void saveSession();     // ä¿å­˜ä¼šè¯ä¿¡æ¯åˆ°æ–‡ä»¶
    void clearSession();    // æ¸…除会话文件
    // é…ç½®æ–‡ä»¶å¤¹è·¯å¾„管理
    static std::string getConfigFolderPath();
    static std::string getSessionFilePath();
    static std::string getDatabaseFilePath();
    // æ›´æ–°æœ€åŽæ´»åŠ¨æ—¶é—´ï¼ˆç”¨äºŽæ— æ“ä½œè¶…æ—¶æ£€æµ‹ï¼‰
    void updateActivityTime();
    // è®¾ç½®ç”¨æˆ·çš„æ— æ“ä½œè¶…æ—¶æ—¶é—´
    void setSessionTimeout(std::chrono::minutes timeout);
    // æ£€æŸ¥æ˜¯å¦æ— æ“ä½œè¶…æ—¶
    bool isInactiveTimeout() const;
    // åˆå§‹åŒ–无操作检测(设置全局钩子和定时器)
    void initializeIdleDetection(HWND hwnd);
    // ç»ˆæ­¢æ— æ“ä½œæ£€æµ‹ï¼ˆæ¸…除钩子和定时器)
    void terminateIdleDetection();
    // èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·å
    std::string getCurrentUser() const;
    // ä¿®æ”¹å½“前登录用户名
    void setCurrentUser(const std::string& strName);
    // èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·å¯†ç 
    std::string getCurrentPass() const;
    // ä¿®æ”¹å½“前登录用户密码
    void setCurrentPass(const std::string& strPass);
    // èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·è§’è‰²
    UserRole getCurrentUserRole() const;
    // ä¿®æ”¹å½“前登录用户角色
    void setCurrentUserRole(UserRole emRole);
    // èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·çš„æ— æ“ä½œè¶…æ—¶æ—¶é—´
    std::chrono::minutes getSessionTimeout() const;
    // èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·çš„ä¼šè¯è¿‡æœŸæ—¶é—´
    std::chrono::hours getSessionExpiration() const;
private:
    UserManager();
    ~UserManager();
    // åˆå§‹åŒ–数据库连接和用户表
    bool initializeDatabase();
    // å“ˆå¸Œå¯†ç ï¼Œç”¨äºŽåŠ å¯†ç”¨æˆ·å¯†ç 
    std::string hashPassword(const std::string& password);
    // åŠ å¯†å’Œè§£å¯†å‡½æ•°
    std::string simpleEncryptDecrypt(const std::string& data, const std::string& key);
    // é”®ç›˜å’Œé¼ æ ‡é’©å­å‡½æ•°
    static LRESULT CALLBACK LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam);
    static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
    // å±žæ€§å®šä¹‰
    std::string m_strCurrentUser;                     // å½“前登录用户名
    std::string m_strCurrentPass;                     // å½“前登录密码
    UserRole m_enCurrentUserRole;                     // å½“前登录用户角色
    bool m_isLoggedIn;                                // æ˜¯å¦å·²ç™»å½•
    bool m_isRememberMe;                              // æ˜¯å¦è®°ä½ç™»å½•状态
    std::chrono::time_point<std::chrono::system_clock> m_tpLastLogin;     // ä¸Šæ¬¡ç™»å½•æ—¶é—´
    std::chrono::time_point<std::chrono::system_clock> m_tpLastActivity;  // æœ€åŽæ´»åŠ¨æ—¶é—´
    std::chrono::minutes m_tmSessionTimeout;          // æ— æ“ä½œè¶…æ—¶æ—¶é—´
    std::chrono::hours m_tmSessionExpiration;         // ä¼šè¯è¿‡æœŸæ—¶é—´
    HHOOK m_hMouseHook;                               // é¼ æ ‡é’©å­å¥æŸ„
    HHOOK m_hKeyboardHook;                            // é”®ç›˜é’©å­å¥æŸ„
    std::unique_ptr<BL::Database> m_pDB;              // æ•°æ®åº“接口
};
#endif // USER_MANAGER_H
SourceCode/Bond/Servo/UserManagerDlg.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,546 @@
// UserManagerDlg.cpp: å®žçŽ°æ–‡ä»¶
//
#include "stdafx.h"
#include "Servo.h"
#include "afxdialogex.h"
#include "UserManagerDlg.h"
#include "InputDialog.h"
#include "UserManager.h"
#include "SystemLogManager.h"
#include "NewCellTypes/GridCellCombo.h"
#include "NewCellTypes/GridCellNumeric.h"
#include <set>
const COLORREF CURR_USER_BK_COLOR = RGB(0, 255, 0);
// CUserManagerDlg å¯¹è¯æ¡†
IMPLEMENT_DYNAMIC(CUserManagerDlg, CBaseDlg)
CUserManagerDlg::CUserManagerDlg(CWnd* pParent /*=nullptr*/)
    : CBaseDlg(IDD_DIALOG_USER_MANAGER, pParent)
{
}
CUserManagerDlg::~CUserManagerDlg()
{
}
void CUserManagerDlg::DoDataExchange(CDataExchange* pDX)
{
    DDX_Control(pDX, IDC_CUSTOM_USER, m_gridUserManager);
    CBaseDlg::DoDataExchange(pDX);
}
void CUserManagerDlg::InitUserManager()
{
    if (m_gridUserManager.GetSafeHwnd() == NULL)
        return;
    int nRows = 1;
    int nCols = 8;
    int nFixRows = 1;
    int nFixCols = 0;
    int nRowIdx = 0;
    int nColIdx = 0;
    m_gridUserManager.DeleteAllItems();
    m_gridUserManager.SetVirtualMode(FALSE);
    m_gridUserManager.GetDefaultCell(TRUE, FALSE)->SetBackClr(g_nGridFixCellColor); // è®¾ç½®å›ºå®šè¡ŒèƒŒæ™¯è‰²
    m_gridUserManager.GetDefaultCell(FALSE, TRUE)->SetBackClr(g_nGridFixCellColor); // è®¾ç½®å›ºå®šåˆ—背景色
    m_gridUserManager.GetDefaultCell(FALSE, FALSE)->SetBackClr(g_nGridCellColor);    // è®¾ç½®å•元格背景色
    m_gridUserManager.SetFixedTextColor(g_nGridFixFontColor); // è®¾ç½®å›ºå®šè¡Œåˆ—字体颜色
    m_gridUserManager.SetRowCount(nRows);
    m_gridUserManager.SetColumnCount(nCols);
    m_gridUserManager.SetFixedRowCount(nFixRows);
    m_gridUserManager.SetFixedColumnCount(nFixCols);
    // Col
    m_gridUserManager.SetColumnWidth(nColIdx, 20);
    m_gridUserManager.SetItemText(nRowIdx, nColIdx++, _T("No."));
    m_gridUserManager.SetColumnWidth(nColIdx, 70);
    m_gridUserManager.SetItemText(nRowIdx, nColIdx++, _T("用户名"));
    m_gridUserManager.SetColumnWidth(nColIdx, 70);
    m_gridUserManager.SetItemText(nRowIdx, nColIdx++, _T("密码"));
    m_gridUserManager.SetColumnWidth(nColIdx, 70);
    m_gridUserManager.SetItemText(nRowIdx, nColIdx++, _T("权限"));
    m_gridUserManager.SetColumnWidth(nColIdx, 70);
    m_gridUserManager.SetItemText(nRowIdx, nColIdx++, _T("会话超时(分钟)"));
    m_gridUserManager.SetColumnWidth(nColIdx, 70);
    m_gridUserManager.SetItemText(nRowIdx, nColIdx++, _T("会话过期(小时)"));
    m_gridUserManager.SetColumnWidth(nColIdx, 70);
    m_gridUserManager.SetItemText(nRowIdx, nColIdx++, _T("最后一次登录时间"));
    m_gridUserManager.SetColumnWidth(nColIdx, 100);
    m_gridUserManager.SetItemText(nRowIdx, nColIdx++, _T("角色描述"));
    m_gridUserManager.SetFixedRowSelection(FALSE);
    m_gridUserManager.SetFixedColumnSelection(FALSE);
    m_gridUserManager.SetEditable(TRUE);
    m_gridUserManager.SetRowResize(FALSE);
    m_gridUserManager.SetColumnResize(TRUE);
    m_gridUserManager.ExpandColumnsToFit(TRUE);
    m_gridUserManager.SetListMode(TRUE);                // å¯ç”¨åˆ—表模式
    m_gridUserManager.EnableSelection(TRUE);            // å¯ç”¨é€‰æ‹©
    m_gridUserManager.SetSingleRowSelection(TRUE);        // è‡ªåŠ¨æ•´è¡Œé«˜äº®ï¼ˆé™åˆ¶ä¸ºå•è¡Œé€‰æ‹©ï¼‰
    m_gridUserManager.ExpandLastColumn();                // æœ€åŽä¸€åˆ—填充网格
    m_mapRoleDescriptions.clear();
    m_mapRoleDescriptions.emplace(_T("管理员"), _T("管理所有用户,分配权限"));
    m_mapRoleDescriptions.emplace(_T("工程师"), _T("维护系统,解决技术问题"));
    m_mapRoleDescriptions.emplace(_T("操作员"), _T("执行日常操作任务"));
    FillUserManager();
}
void CUserManagerDlg::FillUserManager()
{
    UserManager& userManager = UserManager::getInstance();
    if (!userManager.isLoggedIn()) {
        AfxMessageBox(_T("未登录"));
        return;
    }
    if (userManager.getCurrentUserRole() != UserRole::SuperAdmin) {
        AfxMessageBox(_T("非管理员用户"));
        return;
    }
    int nCurrNameRow = -1;
    std::vector<std::vector<std::string>> usersData = userManager.getUsers();
    if (!usersData.empty()) {
        m_gridUserManager.SetRowCount((int)usersData.size() + 1);
        for (int i = 0; i < usersData.size(); i++) {
            int nRowIdx = i + 1;
            int nColIdx = 0;
            m_gridUserManager.SetItemText(nRowIdx, nColIdx++, std::to_string(i + 1).c_str());
            for (int j = 0; j < usersData[i].size(); j++) {
                if (usersData[i][1].empty()) {
                    continue;
                }
                m_gridUserManager.SetItemText(nRowIdx, nColIdx++, usersData[i][j].c_str());
            }
            // å½“前登录用户高亮
            if (nCurrNameRow == -1 && usersData[i][0] == userManager.getCurrentUser()) {
                nCurrNameRow = nRowIdx;
            }
        }
    }
    CStringArray permissions;
    permissions.Add(_T("管理员"));
    permissions.Add(_T("工程师"));
    permissions.Add(_T("操作员"));
    int nCols = m_gridUserManager.GetColumnCount();
    for (int i = 1; i < m_gridUserManager.GetRowCount(); ++i) {
        m_gridUserManager.SetItemState(i, 0, GVIS_READONLY); // ç¬¬ä¸€åˆ—只读
        m_gridUserManager.SetItemState(i, nCols - 2, GVIS_READONLY); // å€’数第二列只读
        m_gridUserManager.SetItemState(i, nCols - 1, GVIS_READONLY); // æœ€åŽä¸€åˆ—只读
        // ç¬¬å››åˆ—设置权限列为下拉框
        if (m_gridUserManager.SetCellType(i, 3, RUNTIME_CLASS(CGridCellCombo))) {
            CGridCellCombo* pCell = static_cast<CGridCellCombo*>(m_gridUserManager.GetCell(i, 3));
            pCell->SetOptions(permissions);
            pCell->SetStyle(CBS_DROPDOWNLIST);
            CString cstrRole = m_gridUserManager.GetItemText(i, 3);
            int nRole = _ttoi(cstrRole);
            if (nRole < 0 || nRole > 2) {
                CString cstrMessage;
                cstrMessage.Format(_T("用户 [%s],权限异常!将设置成操作员!"), m_gridUserManager.GetItemText(i, 1));
                AfxMessageBox(cstrMessage);
                nRole = 2;
            }
            m_gridUserManager.SetItemText(i, 3, permissions.GetAt(nRole));
            auto it = m_mapRoleDescriptions.find(permissions.GetAt(nRole));
            if (it != m_mapRoleDescriptions.end()) {
                m_gridUserManager.SetItemText(i, 7, it->second);
            }
        }
        // ç¬¬äº”、六列(会话超时)设置为Numeric
        //m_gridUserManager.SetCellType(i, 4, RUNTIME_CLASS(CGridCellNumeric));
        //m_gridUserManager.SetCellType(i, 5, RUNTIME_CLASS(CGridCellNumeric));
        m_gridUserManager.SetItemState(i, 4, GVIS_READONLY); // ç¬¬äº”列只读
        m_gridUserManager.SetItemState(i, 5, GVIS_READONLY); // ç¬¬å…­åˆ—只读
        m_gridUserManager.ExpandColumnsToFit(FALSE);
        m_gridUserManager.ExpandLastColumn();
    }
    for (int i = 0; i < nCols; i++) {
        m_gridUserManager.SetItemBkColour(nCurrNameRow, i, CURR_USER_BK_COLOR);
    }
    m_gridUserManager.SetItemState(nCurrNameRow, 3, GVIS_READONLY);
    m_gridUserManager.Invalidate();
    m_gridUserManager.UpdateWindow();
}
void CUserManagerDlg::AddRow(CGridCtrl* pGridCtrl)
{
    if (!pGridCtrl) return;
    if (pGridCtrl->GetRowCount() <= 0 || pGridCtrl->GetColumnCount() <= 0) {
        AfxMessageBox(_T("网格控件未正确初始化,请检查初始化逻辑!"));
        return;
    }
    CInputDialog inputDialog(_T("添加用户"), _T("请输入用户名:"));
    if (inputDialog.DoModal() != IDOK) {
        return;
    }
    CString inputText = inputDialog.GetInputText();
    if (inputText.IsEmpty()) {
        AfxMessageBox(_T("用户名不能为空!"));
        return;
    }
    if (IsUsernameDuplicate(inputText)) {
        AfxMessageBox(_T("用户名重复!"));
        return;
    }
    int nRowCount = pGridCtrl->GetRowCount();
    pGridCtrl->SetRowCount(nRowCount + 1);
    int newRowIndex = nRowCount;
    CString strText;
    strText.Format(_T("%d"), newRowIndex);
    pGridCtrl->SetItemText(newRowIndex, 0, strText);
    pGridCtrl->SetItemText(newRowIndex, 1, inputText);
    pGridCtrl->SetItemText(newRowIndex, 2, _T("123456"));
    pGridCtrl->SetItemText(newRowIndex, 3, _T("操作员"));
    pGridCtrl->SetItemText(newRowIndex, 4, _T("30"));
    pGridCtrl->SetItemText(newRowIndex, 5, _T("72"));
    pGridCtrl->SetItemText(newRowIndex, 6, _T("2024-01-01 00:00:00"));
    auto it = m_mapRoleDescriptions.find(_T("操作员"));
    if (it != m_mapRoleDescriptions.end()) {
        pGridCtrl->SetItemText(newRowIndex, 7, it->second);
    }
    int nCols = pGridCtrl->GetColumnCount();
    pGridCtrl->SetItemState(newRowIndex, 0, GVIS_READONLY); // ç¬¬ä¸€åˆ—只读
    pGridCtrl->SetItemState(newRowIndex, nCols - 2, GVIS_READONLY); // å€’数第二列只读
    pGridCtrl->SetItemState(newRowIndex, nCols - 1, GVIS_READONLY); // æœ€åŽä¸€åˆ—只读
    // ç¬¬å››åˆ—设置(权限列)为下拉框
    CStringArray permissions;
    permissions.Add(_T("管理员"));
    permissions.Add(_T("工程师"));
    permissions.Add(_T("操作员"));
    if (pGridCtrl->SetCellType(newRowIndex, 3, RUNTIME_CLASS(CGridCellCombo))) {
        CGridCellCombo* pCell = static_cast<CGridCellCombo*>(pGridCtrl->GetCell(newRowIndex, 3));
        pCell->SetOptions(permissions);
        pCell->SetStyle(CBS_DROPDOWNLIST);
        pGridCtrl->SetItemText(newRowIndex, 3, permissions.GetAt(2));
    }
    // ç¬¬äº”、六列(会话超时)设置为Numeric
    //pGridCtrl->SetCellType(newRowIndex, 4, RUNTIME_CLASS(CGridCellNumeric));
    //pGridCtrl->SetCellType(newRowIndex, 5, RUNTIME_CLASS(CGridCellNumeric));
    pGridCtrl->SetItemState(newRowIndex, 4, GVIS_READONLY); // ç¬¬äº”列只读
    pGridCtrl->SetItemState(newRowIndex, 5, GVIS_READONLY); // ç¬¬å…­åˆ—只读
    pGridCtrl->ExpandColumnsToFit(FALSE);
    pGridCtrl->ExpandLastColumn();
    pGridCtrl->Invalidate();
    pGridCtrl->UpdateWindow();
    CString cstrMessage;
    cstrMessage.Format(_T("预添加用户 [%s]!"), inputText);
    std::string strMessage = CT2A(cstrMessage);
    SystemLogManager::getInstance().log(SystemLogManager::LogType::Operation, strMessage);
}
void CUserManagerDlg::DeleteSelectedRow(CGridCtrl* pGridCtrl)
{
    if (!pGridCtrl) return;
    CCellRange selectedRange = pGridCtrl->GetSelectedCellRange();
    if (selectedRange.IsValid()) {
        CString currentUser = UserManager::getInstance().getCurrentUser().c_str();
        std::vector<int> rowsToDelete;
        for (int row = selectedRange.GetMinRow(); row <= selectedRange.GetMaxRow(); ++row) {
            BOOL isRowSelected = FALSE;
            for (int col = selectedRange.GetMinCol(); col <= selectedRange.GetMaxCol(); ++col) {
                if (pGridCtrl->IsCellSelected(row, col)) {
                    isRowSelected = TRUE;
                    break;
                }
            }
            if (!isRowSelected) {
                continue;
            }
            CString selectedUser = pGridCtrl->GetItemText(row, 1);
            if (selectedUser == currentUser) {
                CString message;
                message.Format(_T("用户 [%s] æ˜¯å½“前登录用户,不能删除!"), currentUser);
                AfxMessageBox(message);
                return;
            }
            rowsToDelete.push_back(row);
        }
        if (rowsToDelete.empty()) {
            AfxMessageBox(_T("请先选择要删除的行!"));
            return;
        }
        CString message;
        if (rowsToDelete.size() == 1) {
            CString selectedUser = pGridCtrl->GetItemText(rowsToDelete[0], 1);
            message.Format(_T("确定要删除选中用户 [%s] å—?"), selectedUser);
            CString cstrMessage;
            cstrMessage.Format(_T("预删除用户 [%s]!"), selectedUser);
            std::string strMessage = CT2A(cstrMessage);
            SystemLogManager::getInstance().log(SystemLogManager::LogType::Operation, strMessage);
        }
        else {
            message.Format(_T("确定要删除选中的 %d ä¸ªç”¨æˆ·å—?"), rowsToDelete.size());
        }
        if (AfxMessageBox(message, MB_YESNO | MB_ICONQUESTION) == IDYES) {
            for (auto it = rowsToDelete.rbegin(); it != rowsToDelete.rend(); ++it) {
                pGridCtrl->DeleteRow(*it);
            }
            pGridCtrl->ExpandColumnsToFit(FALSE);
            pGridCtrl->ExpandLastColumn();
            pGridCtrl->Invalidate();
            pGridCtrl->UpdateWindow();
        }
    }
    else {
        AfxMessageBox(_T("请先选择要删除的用户!"));
    }
}
bool CUserManagerDlg::IsUsernameDuplicate(const CString& username)
{
    for (int row = 1; row < m_gridUserManager.GetRowCount(); ++row) {
        if (m_gridUserManager.GetItemText(row, 1) == username) {
            return true;
        }
    }
    return false;
}
BEGIN_MESSAGE_MAP(CUserManagerDlg, CBaseDlg)
    ON_NOTIFY(GVN_COMBOSELCHANGE, IDC_CUSTOM_USER, &CUserManagerDlg::OnGridComboSelChange)
    ON_BN_CLICKED(IDC_BUTTON_ADD, &CUserManagerDlg::OnBnClickedButtonAdd)
    ON_BN_CLICKED(IDC_BUTTON_DEL, &CUserManagerDlg::OnBnClickedButtonDel)
    ON_BN_CLICKED(IDOK, &CUserManagerDlg::OnBnClickedOk)
    ON_BN_CLICKED(IDC_BUTTON_INSERT, &CUserManagerDlg::OnBnClickedButtonInsert)
END_MESSAGE_MAP()
// CUserManagerDlg æ¶ˆæ¯å¤„理程序
BOOL CUserManagerDlg::OnInitDialog()
{
    CBaseDlg::OnInitDialog();
    // TODO:  åœ¨æ­¤æ·»åŠ é¢å¤–çš„åˆå§‹åŒ–
    SetWindowText(_T("用户管理"));
    // åˆå§‹åŒ–用户管理表格
    InitUserManager();
    return TRUE;  // return TRUE unless you set the focus to a control
    // å¼‚常: OCX å±žæ€§é¡µåº”返回 FALSE
}
void CUserManagerDlg::OnGridComboSelChange(NMHDR* pNMHDR, LRESULT* pResult)
{
    NM_GRIDVIEW* pItem = (NM_GRIDVIEW*)pNMHDR;
    int nRow = pItem->iRow;
    int nCol = pItem->iColumn;
    // ç¬¬4列为权限列, ç¬¬8列为角色描述列, ä»Žæ˜ å°„中查找对应描述
    if (nCol == 3) {
        if (m_gridUserManager.GetItemBkColour(nRow, nCol) == CURR_USER_BK_COLOR) {
            AfxMessageBox(_T("当前登录用户权限不能修改!"));
        }
        else {
            CString selectedRole = m_gridUserManager.GetItemText(nRow, nCol);
            auto it = m_mapRoleDescriptions.find(selectedRole);
            if (it != m_mapRoleDescriptions.end()) {
                m_gridUserManager.SetItemText(nRow, 7, it->second);
                m_gridUserManager.RedrawCell(nRow, 7);
            }
            else {
                m_gridUserManager.SetItemText(nRow, 7, _T(""));
                m_gridUserManager.RedrawCell(nRow, 7);
            }
        }
    }
    *pResult = 0;
}
void CUserManagerDlg::OnBnClickedButtonAdd()
{
    // TODO: åœ¨æ­¤æ·»åŠ æŽ§ä»¶é€šçŸ¥å¤„ç†ç¨‹åºä»£ç 
    AddRow(&m_gridUserManager);
}
void CUserManagerDlg::OnBnClickedButtonInsert()
{
    // TODO: åœ¨æ­¤æ·»åŠ æŽ§ä»¶é€šçŸ¥å¤„ç†ç¨‹åºä»£ç 
    if (!m_gridUserManager.GetSafeHwnd()) return;
    CCellRange selectedRange = m_gridUserManager.GetSelectedCellRange();
    if (!selectedRange.IsValid()) {
        AfxMessageBox(_T("请先选择要插入的位置!"));
        return;
    }
    int minRow = selectedRange.GetMinRow();
    int maxRow = selectedRange.GetMaxRow();
    if (minRow < 1) {
        AfxMessageBox(_T("请选择有效的行!"));
        return;
    }
    for (int row = maxRow; row >= minRow; --row) {
        std::vector<CString> rowData;
        for (int col = 0; col < m_gridUserManager.GetColumnCount(); ++col) {
            rowData.push_back(m_gridUserManager.GetItemText(row, col));
        }
        m_gridUserManager.InsertRow(_T("新用户"), row);
        CString newUsername = rowData[1];
        int suffix = 1;
        while (IsUsernameDuplicate(newUsername)) {
            newUsername.Format(_T("%s_%d"), rowData[1], suffix++);
        }
        rowData[1] = newUsername;
        for (int col = 0; col < m_gridUserManager.GetColumnCount(); ++col) {
            m_gridUserManager.SetItemText(row, col, rowData[col]);
        }
        CStringArray permissions;
        permissions.Add(_T("管理员"));
        permissions.Add(_T("工程师"));
        permissions.Add(_T("操作员"));
        if (m_gridUserManager.SetCellType(row, 3, RUNTIME_CLASS(CGridCellCombo))) {
            CGridCellCombo* pCell = static_cast<CGridCellCombo*>(m_gridUserManager.GetCell(row, 3));
            pCell->SetOptions(permissions);
            pCell->SetStyle(CBS_DROPDOWNLIST);
            m_gridUserManager.SetItemText(row, 3, rowData[3]);
        }
        m_gridUserManager.SetCellType(row, 4, RUNTIME_CLASS(CGridCellNumeric));
        m_gridUserManager.SetCellType(row, 5, RUNTIME_CLASS(CGridCellNumeric));
    }
    for (int row = 1; row < m_gridUserManager.GetRowCount(); ++row) {
        CString strIndex;
        strIndex.Format(_T("%d"), row);
        m_gridUserManager.SetItemText(row, 0, strIndex);
    }
    m_gridUserManager.Invalidate();
    m_gridUserManager.UpdateWindow();
}
void CUserManagerDlg::OnBnClickedButtonDel()
{
    // TODO: åœ¨æ­¤æ·»åŠ æŽ§ä»¶é€šçŸ¥å¤„ç†ç¨‹åºä»£ç 
    DeleteSelectedRow(&m_gridUserManager);
}
void CUserManagerDlg::OnBnClickedOk()
{
    // TODO: åœ¨æ­¤æ·»åŠ æŽ§ä»¶é€šçŸ¥å¤„ç†ç¨‹åºä»£ç 
    std::vector<std::vector<std::string>> vecData;
    std::set<std::string> usernameSet; // ç”¨äºŽå­˜å‚¨ç”¨æˆ·åï¼Œæ£€æŸ¥æ˜¯å¦é‡å¤
    int nCurrUserRow = -1;
    UserManager& userManager = UserManager::getInstance();
    for (int i = 1; i < m_gridUserManager.GetRowCount(); ++i) {
        std::vector<std::string> rowData;
        CString cellText = m_gridUserManager.GetItemText(i, 1);
        std::string username = CT2A(cellText.GetString());
        cellText = m_gridUserManager.GetItemText(i, 2);
        std::string userpass = CT2A(cellText.GetString());
        if (username.empty() || userpass.empty()) {
            AfxMessageBox(_T("用户名和密码不能为空!"));
            return;
        }
        if (usernameSet.find(username) != usernameSet.end()) {
            CString message;
            message.Format(_T("用户名 [%s] é‡å¤ï¼Œè¯·ä¿®æ”¹åŽä¿å­˜ï¼"), cellText);
            AfxMessageBox(message, MB_ICONEXCLAMATION);
            return;
        }
        usernameSet.insert(username);
        if (nCurrUserRow == -1 && m_gridUserManager.GetItemBkColour(i, 0) == CURR_USER_BK_COLOR){
            nCurrUserRow = i;
            if (username.compare(userManager.getCurrentUser()) != 0) {
                userManager.setCurrentUser(username);
            }
            if (userpass.compare(userManager.getCurrentPass()) != 0) {
                userManager.setCurrentPass(userpass);
            }
            userManager.clearSession();
            userManager.saveSession();
        }
        for (int j = 1; j < m_gridUserManager.GetColumnCount() - 1; ++j) {
            CString cellText = m_gridUserManager.GetItemText(i, j);
            std::string cellString = CT2A(cellText.GetString());
            // ç¬¬4列是权限,转换为数字字符串
            if (j == 3) {
                if (cellText == _T("管理员"))
                    cellString = "0";
                else if (cellText == _T("工程师"))
                    cellString = "1";
                else if (cellText == _T("操作员"))
                    cellString = "2";
                else
                    cellString = "2";
            }
            rowData.push_back(cellString);
        }
        vecData.push_back(rowData);
    }
    userManager.setUsers(vecData);
    CBaseDlg::OnOK();
}
SourceCode/Bond/Servo/UserManagerDlg.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
#pragma once
#include "afxdialogex.h"
#include "GridCtrl.h"
#include "CBaseDlg.h"
// CUserManagerDlg å¯¹è¯æ¡†
class CUserManagerDlg : public CBaseDlg
{
    DECLARE_DYNAMIC(CUserManagerDlg)
public:
    CUserManagerDlg(CWnd* pParent = nullptr);   // æ ‡å‡†æž„造函数
    virtual ~CUserManagerDlg();
// å¯¹è¯æ¡†æ•°æ®
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_DIALOG_USER_MANAGER };
#endif
private:
    void InitUserManager();
    void FillUserManager();
    void AddRow(CGridCtrl* pGridCtrl);
    void DeleteSelectedRow(CGridCtrl* pGridCtrl);
    bool IsUsernameDuplicate(const CString& username);
private:
    std::map<CString, CString> m_mapRoleDescriptions;
    // æŽ§ä»¶
    CGridCtrl m_gridUserManager;
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV æ”¯æŒ
    DECLARE_MESSAGE_MAP()
public:
    virtual BOOL OnInitDialog();
    afx_msg void OnGridComboSelChange(NMHDR* pNMHDR, LRESULT* pResult);
    afx_msg void OnBnClickedButtonAdd();
    afx_msg void OnBnClickedButtonInsert();
    afx_msg void OnBnClickedButtonDel();
    afx_msg void OnBnClickedOk();
};
SourceCode/Bond/Servo/resource.h
Binary files differ