chenluhua1980
2025-11-18 d60bb6116c698269d405fce3040bdc75128c6820
1.用户管理相关;
已添加7个文件
已修改10个文件
585 ■■■■■ 文件已修改
.gitignore 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CUserManager2.cpp 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CUserManager2.h 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CUserManager2Dlg.cpp 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CUserManager2Dlg.h 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/LoginDlg2.cpp 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/LoginDlg2.h 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.cpp 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.rc 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.vcxproj 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.vcxproj.filters 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoDlg.cpp 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ToolUnits.cpp 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ToolUnits.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/resource.h 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/stdafx.h 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/USERXLibrary/UserXAPI.h 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -63,3 +63,4 @@
SourceCode/Bond/x64/Debug/HsmsPassive.cache
SourceCode/Bond/x64/Debug/MasterState.dat
SourceCode/Bond/x64/Debug/Recipe/EQ10_Unit0.recipelist
SourceCode/Bond/UserX/
SourceCode/Bond/Servo/CUserManager2.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,100 @@
#include "stdafx.h"
#include "CUserManager2.h"
#include "ToolUnits.h"
#include <sstream>
#include <cwchar>
std::vector<std::wstring> SplitLines(const std::wstring& text)
{
    std::wstringstream ss(text); std::vector<std::wstring> v; std::wstring line; while (std::getline(ss, line)) v.push_back(line); return v;
}
template<typename Fn>
std::wstring ReadBufferVia(Fn fn)
{
    int need = fn(nullptr, 0); if (need <= 0) return L"";
    std::wstring buf; buf.resize((size_t)need);
    int rc = fn(buf.data(), need);
    if (rc == 0) { if (!buf.empty() && buf.back() == L'\0') buf.pop_back(); return buf; }
    return L"";
}
// èŽ·å–å•ä¾‹å®žä¾‹
CUserManager2& CUserManager2::getInstance() {
    static CUserManager2 instance;
    return instance;
}
CUserManager2::CUserManager2()
{
}
CUserManager2::~CUserManager2()
{
}
void CUserManager2::init(const char* pszDir)
{
    std::wstring dir = CToolUnits::AnsiToWString(std::string(pszDir));
    UX_Init(dir.c_str());
    wchar_t buffer[1024];
    UX_GetUsers(buffer, 1024);
    bool hasAny = false;
    for (auto& ln : SplitLines(buffer)) { if (!ln.empty()) { hasAny = true; break; } }
    if (!hasAny) {
        const wchar_t* roles = L"Admin:100\nEngineer:50\nOperator:10\n";
        (void)UX_SetRoleDefinitions(roles);
        (void)UX_AddUser(L"admin", L"Administrator", L"admin123", L"Admin");
    }
}
bool CUserManager2::login(const char* pszAccount, const char* pszPwd)
{
    std::wstring strUser, strPwd;
    strUser = CToolUnits::AnsiToWString(std::string(pszAccount));
    strPwd = CToolUnits::AnsiToWString(std::string(pszPwd));
    int rc = UX_Login(strUser.c_str(), strPwd.c_str());
    return rc == UX_OK;
}
bool CUserManager2::isLoggedIn()
{
    return UX_IsLoggedIn();
}
std::string CUserManager2::getCurrentUserName()
{
    std::string strName;
    int need = UX_GetCurrentUser(nullptr, 0);
    std::wstring buf; buf.resize((size_t)need);
    if (UX_GetCurrentUser(buf.data(), need) == UX_OK) {
        if (!buf.empty() && buf.back() == L'\0')
            buf.pop_back();
        strName = CToolUnits::WStringToAnsi(buf);
    }
    return strName;
}
bool CUserManager2::IsAdminCurrent()
{
    if (UX_IsLoggedIn() != 1) return false;
    int need = UX_GetCurrentUser(nullptr, 0); if (need <= 0) return false;
    std::wstring user; user.resize((size_t)need);
    if (UX_GetCurrentUser(user.data(), need) != 0) return false;
    if (!user.empty() && user.back() == L'\0') user.pop_back();
    int maxLvl = 0; auto rolesTxt = ReadBufferVia([](wchar_t* b, int n) { return UX_GetRoles(b, n); });
    for (auto& ln : SplitLines(rolesTxt)) { size_t p = ln.find(L':'); if (p != std::wstring::npos) { int lvl = _wtoi(ln.substr(p + 1).c_str()); if (lvl > maxLvl) maxLvl = lvl; } }
    int myLvl = 0; auto usersTxt = ReadBufferVia([](wchar_t* b, int n) { return UX_GetUsers(b, n); });
    for (auto& ln : SplitLines(usersTxt)) {
        if (ln.empty()) continue; size_t p1 = ln.find(L','), p2 = ln.find(L',', p1 == std::wstring::npos ? 0 : p1 + 1), p3 = ln.find(L',', p2 == std::wstring::npos ? 0 : p2 + 1);
        std::wstring name = (p1 == std::wstring::npos ? ln : ln.substr(0, p1)); if (name == user) { if (p2 != std::wstring::npos) { std::wstring lvlS = ln.substr(p2 + 1, (p3 == std::wstring::npos ? ln.size() : p3) - (p2 + 1)); myLvl = _wtoi(lvlS.c_str()); } break; }
    }
    return (maxLvl > 0) && (myLvl >= maxLvl);
}
SourceCode/Bond/Servo/CUserManager2.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
#pragma once
class CUserManager2
{
public:
    static CUserManager2& getInstance();
    CUserManager2(const CUserManager2&) = delete;
    CUserManager2& operator=(const CUserManager2&) = delete;
public:
    void init(const char* pszDir);
    bool login(const char* pszAccount, const char* pszPwd);
    bool isLoggedIn();
    std::string getCurrentUserName();
    bool IsAdminCurrent();
private:
    CUserManager2();
    ~CUserManager2();
};
SourceCode/Bond/Servo/CUserManager2Dlg.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
// CUserManager2Dlg.cpp: å®žçŽ°æ–‡ä»¶
//
#include "stdafx.h"
#include "Servo.h"
#include "CUserManager2Dlg.h"
#include "afxdialogex.h"
// CUserManager2Dlg å¯¹è¯æ¡†
IMPLEMENT_DYNAMIC(CUserManager2Dlg, CDialogEx)
CUserManager2Dlg::CUserManager2Dlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_DIALOG_USER_MANAGER2, pParent)
{
}
CUserManager2Dlg::~CUserManager2Dlg()
{
}
void CUserManager2Dlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CUserManager2Dlg, CDialogEx)
    ON_WM_SIZE()
END_MESSAGE_MAP()
// CUserManager2Dlg æ¶ˆæ¯å¤„理程序
BOOL CUserManager2Dlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    // TODO:  åœ¨æ­¤æ·»åŠ é¢å¤–çš„åˆå§‹åŒ–
    return TRUE;  // return TRUE unless you set the focus to a control
                  // å¼‚常: OCX å±žæ€§é¡µåº”返回 FALSE
}
void CUserManager2Dlg::OnSize(UINT nType, int cx, int cy)
{
    CDialogEx::OnSize(nType, cx, cy);
    // TODO: åœ¨æ­¤å¤„添加消息处理程序代码
}
SourceCode/Bond/Servo/CUserManager2Dlg.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
#pragma once
// CUserManager2Dlg å¯¹è¯æ¡†
class CUserManager2Dlg : public CDialogEx
{
    DECLARE_DYNAMIC(CUserManager2Dlg)
public:
    CUserManager2Dlg(CWnd* pParent = nullptr);   // æ ‡å‡†æž„造函数
    virtual ~CUserManager2Dlg();
// å¯¹è¯æ¡†æ•°æ®
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_DIALOG_USER_MANAGER2 };
#endif
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV æ”¯æŒ
    DECLARE_MESSAGE_MAP()
public:
    virtual BOOL OnInitDialog();
    afx_msg void OnSize(UINT nType, int cx, int cy);
};
SourceCode/Bond/Servo/LoginDlg2.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,98 @@
// LoginDlg.cpp: å®žçŽ°æ–‡ä»¶
//
#include "stdafx.h"
#include "Servo.h"
#include "afxdialogex.h"
#include "LoginDlg2.h"
// CLoginDlg å¯¹è¯æ¡†
IMPLEMENT_DYNAMIC(CLoginDlg2, CDialogEx)
CLoginDlg2::CLoginDlg2(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_DIALOG_LOGIN, pParent)
{
}
CLoginDlg2::~CLoginDlg2()
{
}
void CLoginDlg2::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CLoginDlg2, CDialogEx)
    ON_BN_CLICKED(IDC_BUTTON_LOGIN, &CLoginDlg2::OnBnClickedLogin)
    ON_STN_CLICKED(IDC_STATIC_CHANGE_PASSWORD, &CLoginDlg2::OnBnClickedChangePassword)
END_MESSAGE_MAP()
// CLoginDlg æ¶ˆæ¯å¤„理程序
BOOL CLoginDlg2::OnInitDialog()
{
    CDialog::OnInitDialog();
    // è®¾ç½®çª—口标题和初始值
    SetWindowText(_T("登录"));
    CStatic* pStaticImage = (CStatic*)GetDlgItem(IDC_STATIC_IMAGE);
    ASSERT(pStaticImage);
    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 æŽ§ä»¶ä¸ºå›¾æ ‡æ ·å¼
        pStaticImage->ModifyStyle(0xF, SS_ICON);
        pStaticImage->SetIcon(hIcon);
    }
    // æ·»åŠ SS_NOTIFY样式
    CStatic* pStatic = (CStatic*)GetDlgItem(IDC_STATIC_CHANGE_PASSWORD);
    if (pStatic != nullptr) {
        pStatic->ModifyStyle(0, SS_NOTIFY);
    }
    GetDlgItem(IDC_CHECK_REMEMBER_PASSWORD)->ShowWindow(SW_HIDE);
    return TRUE;
}
void CLoginDlg2::OnBnClickedLogin()
{
    GetDlgItemText(IDC_EDIT_USERNAME, m_strUsername);
    GetDlgItemText(IDC_EDIT_PASSWORD, m_strPassword);
    if (m_strUsername.IsEmpty()) {
        AfxMessageBox(_T("请输入用户名"));
        GetDlgItem(IDC_EDIT_USERNAME)->SetFocus();
        return;
    }
    if (m_strPassword.IsEmpty()) {
        AfxMessageBox(_T("请输入密码"));
        GetDlgItem(IDC_EDIT_PASSWORD)->SetFocus();
        return;
}
    EndDialog(IDOK);
}
void CLoginDlg2::OnBnClickedChangePassword()
{
}
SourceCode/Bond/Servo/LoginDlg2.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
#pragma once
#include "afxdialogex.h"
// CLoginDlg å¯¹è¯æ¡†
class CLoginDlg2 : public CDialogEx
{
    DECLARE_DYNAMIC(CLoginDlg2)
public:
    CLoginDlg2(CWnd* pParent = nullptr);   // æ ‡å‡†æž„造函数
    virtual ~CLoginDlg2();
// å¯¹è¯æ¡†æ•°æ®
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_DIALOG_LOGIN };
#endif
public:
    CString m_strUsername;
    CString m_strPassword;
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.cpp
@@ -17,6 +17,8 @@
#include "MapPosWnd.h"
#include "HmTab.h"
#include "CControlJobManagerDlg.h"
#include "ToolUnits.h"
#include "CUserManager2.h"
// å£°æ˜Žå…¨å±€å˜é‡ï¼Œç”¨äºŽç®¡ç† GDI+ åˆå§‹åŒ–
@@ -100,6 +102,11 @@
    m_strAppDir = CString(sDrive) + CString(sDir);
    m_strAppFile = CString(sFilename);
    m_model.setWorkDir((LPTSTR)(LPCTSTR)m_strAppDir);
    // ç”¨æˆ·æ•°æ®åº“管理
    CString strDir = m_strAppDir + _T("\\DB");
    CUserManager2::getInstance().init((LPTSTR)(LPCTSTR)strDir);
    // æ³¨å†ŒæŽ§ä»¶
@@ -233,6 +240,7 @@
    m_model.term();
    HSMS_Term();
    RX_Term();
    UX_Shutdown();
    // æ¸…理 GDI+
    TermGDIPlus();
SourceCode/Bond/Servo/Servo.rc
Binary files differ
SourceCode/Bond/Servo/Servo.vcxproj
@@ -255,6 +255,8 @@
    <ClInclude Include="CRobotTaskDlg.h" />
    <ClInclude Include="CSVData.h" />
    <ClInclude Include="CServoUtilsTool.h" />
    <ClInclude Include="CUserManager2.h" />
    <ClInclude Include="CUserManager2Dlg.h" />
    <ClInclude Include="CVariable.h" />
    <ClInclude Include="DeviceRecipeParamDlg.h" />
    <ClInclude Include="GlassJson.h" />
@@ -280,6 +282,7 @@
    <ClInclude Include="InputDialog.h" />
    <ClInclude Include="JobSlotGrid.h" />
    <ClInclude Include="LoginDlg.h" />
    <ClInclude Include="LoginDlg2.h" />
    <ClInclude Include="MsgDlg.h" />
    <ClInclude Include="PageRecipe.h" />
    <ClInclude Include="CDoubleGlass.h" />
@@ -468,6 +471,8 @@
    <ClCompile Include="CRobotTaskDlg.cpp" />
    <ClCompile Include="CSVData.cpp" />
    <ClCompile Include="CServoUtilsTool.cpp" />
    <ClCompile Include="CUserManager2.cpp" />
    <ClCompile Include="CUserManager2Dlg.cpp" />
    <ClCompile Include="CVariable.cpp" />
    <ClCompile Include="DeviceRecipeParamDlg.cpp" />
    <ClCompile Include="GlassJson.cpp" />
@@ -491,6 +496,7 @@
    <ClCompile Include="InputDialog.cpp" />
    <ClCompile Include="JobSlotGrid.cpp" />
    <ClCompile Include="LoginDlg.cpp" />
    <ClCompile Include="LoginDlg2.cpp" />
    <ClCompile Include="MsgDlg.cpp" />
    <ClCompile Include="PageRecipe.cpp" />
    <ClCompile Include="CDoubleGlass.cpp" />
SourceCode/Bond/Servo/Servo.vcxproj.filters
@@ -227,6 +227,10 @@
    <ClCompile Include="..\DAQBridge\proto\ProtocolCodec.cpp">
      <Filter>DAQBridge</Filter>
    </ClCompile>
    <ClCompile Include="ClientListDlg.cpp" />
    <ClCompile Include="LoginDlg2.cpp" />
    <ClCompile Include="CUserManager2.cpp" />
    <ClCompile Include="CUserManager2Dlg.cpp" />
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="AlarmManager.h" />
@@ -497,6 +501,10 @@
    <ClInclude Include="..\DAQBridge\DAQConfig.h">
      <Filter>DAQBridge</Filter>
    </ClInclude>
    <ClInclude Include="ClientListDlg.h" />
    <ClInclude Include="LoginDlg2.h" />
    <ClInclude Include="CUserManager2.h" />
    <ClInclude Include="CUserManager2Dlg.h" />
  </ItemGroup>
  <ItemGroup>
    <ResourceCompile Include="Servo.rc" />
SourceCode/Bond/Servo/ServoDlg.cpp
@@ -16,6 +16,7 @@
#include "CRobotCmdContainerDlg.h"
#include "CRobotCmdTestDlg.h"
#include "LoginDlg.h"
#include "LoginDlg2.h"
#include "ChangePasswordDlg.h"
#include "UserManagerDlg.h"
#include "SystemLogManagerDlg.h"
@@ -30,6 +31,8 @@
#include "InputDialog.h"
#include "ClientListDlg.h"
#include "CControlJobManagerDlg.h"
#include "CUserManager2.h"
#include "CUserManager2Dlg.h"
#ifdef _DEBUG
@@ -44,7 +47,7 @@
#define TIMER_ID_UPDATE_RUMTIME            2
/* Test */
#define TIMER_ID_TEST                    3
#define TIMER_ID_LOGIN                    3
// ç”¨äºŽåº”用程序“关于”菜单项的 CAboutDlg å¯¹è¯æ¡†
@@ -197,7 +200,7 @@
            else if (RX_CODE_MASTER_STATE_CHANGED == code) {
                SERVO::MASTERSTATE state = theApp.m_model.getMaster().getState();
                if (state == SERVO::MASTERSTATE::READY) {
                    m_pTopToolbar->GetBtn(IDC_BUTTON_RUN)->EnableWindow(TRUE);
                    m_pTopToolbar ->GetBtn(IDC_BUTTON_RUN)->EnableWindow(TRUE);
                    m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_BATCH)->EnableWindow(TRUE);
                    m_pTopToolbar->GetBtn(IDC_BUTTON_RUN_CT)->EnableWindow(TRUE);
                    m_pTopToolbar->GetBtn(IDC_BUTTON_STOP)->EnableWindow(FALSE);
@@ -368,7 +371,7 @@
    // model init
    theApp.m_model.init();
    SetTimer(TIMER_ID_TEST, 1000, nullptr);
    SetTimer(TIMER_ID_LOGIN, 1000, nullptr);
    // èœå•
    CMenu menu;
@@ -935,11 +938,23 @@
        m_pMyStatusbar->setRunTimeText((LPTSTR)(LPCTSTR)strText);
    }
    else if(TIMER_ID_TEST == nIDEvent){
        static __int64 tttt = 0;
        tttt++;
        theApp.m_model.m_hsmsPassive.setVariableValue("CJobSpace", tttt % 10);
        theApp.m_model.m_hsmsPassive.setVariableValue("PJobSpace", tttt % 5);
    else if(TIMER_ID_LOGIN == nIDEvent){
        KillTimer(TIMER_ID_LOGIN);
        if (!CUserManager2::getInstance().isLoggedIn()) {
            CLoginDlg2 dlg;
            if (dlg.DoModal() != IDOK) {
                PostMessage(WM_CLOSE);
            }
            else {
                bool bRet = CUserManager2::getInstance().login((LPTSTR)(LPCTSTR)dlg.m_strUsername,
                    (LPTSTR)(LPCTSTR)dlg.m_strPassword);
                if (!bRet) {
                    AfxMessageBox("登录失败,请检查用户名或密码是否正确!");
                    PostMessage(WM_CLOSE);
                }
                UpdateLoginStatus();
            }
        }
    }
@@ -977,32 +992,24 @@
void CServoDlg::UpdateLoginStatus()
{
    HMENU hMenu = m_pTopToolbar->GetOperatorMenu();
    UserManager& userManager = UserManager::getInstance();
    if (userManager.isLoggedIn())
    {
        ::EnableMenuItem(hMenu, ID_OPEATOR_LOGIN, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
        ::EnableMenuItem(hMenu, ID_OPERATOR_CHANGE_PASSWORD, MF_BYCOMMAND | MF_ENABLED);
    CUserManager2& userManager = CUserManager2::getInstance();
    if (userManager.isLoggedIn()) {
        ::EnableMenuItem(hMenu, ID_OPERATOR_SYSTEM_LOG, MF_BYCOMMAND | MF_ENABLED);
        ::EnableMenuItem(hMenu, ID_OPEATOR_SWITCH, MF_BYCOMMAND | MF_ENABLED);
        ::EnableMenuItem(hMenu, ID_OPERATOR_LOGOUT, MF_BYCOMMAND | MF_ENABLED);
        if (userManager.getCurrentUserRole() == UserRole::SuperAdmin) {
        if (userManager.IsAdminCurrent()) {
            ::EnableMenuItem(hMenu, ID_OPEATOR_USER_MANAGER, MF_BYCOMMAND | MF_ENABLED);
        }
        else {
            ::EnableMenuItem(hMenu, ID_OPEATOR_USER_MANAGER, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
        }
        m_pTopToolbar->SetOperatorBtnText(userManager.getCurrentUser().c_str());
        m_pTopToolbar->SetOperatorBtnText(userManager.getCurrentUserName().c_str());
    }
    else {
        ::EnableMenuItem(hMenu, ID_OPEATOR_LOGIN, MF_BYCOMMAND | MF_ENABLED);
        ::EnableMenuItem(hMenu, ID_OPERATOR_CHANGE_PASSWORD, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
        ::EnableMenuItem(hMenu, ID_OPEATOR_USER_MANAGER, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
        ::EnableMenuItem(hMenu, ID_OPERATOR_SYSTEM_LOG, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
        ::EnableMenuItem(hMenu, ID_OPEATOR_SWITCH, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
        ::EnableMenuItem(hMenu, ID_OPERATOR_LOGOUT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
        m_pTopToolbar->SetOperatorBtnText(_T("未登录"));
    }
}
@@ -1090,6 +1097,13 @@
    }
    else if (id == IDC_BUTTON_OPERATOR) {
        int menuId = (int)wParam;
        if (menuId == 0) {
            CUserManager2Dlg dlg;
            dlg.DoModal();
        }
        /*
        SystemLogManager& logManager = SystemLogManager::getInstance();
        UserManager& userManager = UserManager::getInstance();
        if (menuId == 0) {
@@ -1138,6 +1152,7 @@
        }
        UpdateLoginStatus();
        */
    }
    return 0;
SourceCode/Bond/Servo/ToolUnits.cpp
@@ -574,4 +574,44 @@
    std::ostringstream oss;
    oss << std::put_time(&tm, "%Y%m%d%H%M%S"); // ä¾‹ï¼š2025-09-15 08:23:07
    return oss.str();
}
std::wstring CToolUnits::AnsiToWString(const std::string& str)
{
    if (str.empty()) return std::wstring();
    int len = ::MultiByteToWideChar(CP_ACP, 0,
        str.c_str(), -1,
        nullptr, 0);
    if (len <= 0) return std::wstring();
    std::wstring ws;
    ws.resize(len - 1);
    ::MultiByteToWideChar(CP_ACP, 0,
        str.c_str(), -1,
        &ws[0], len);
    return ws;
}
std::string CToolUnits::WStringToAnsi(const std::wstring& wstr)
{
    if (wstr.empty()) return std::string();
    int len = ::WideCharToMultiByte(CP_ACP, 0,
        wstr.c_str(), -1,
        nullptr, 0,
        nullptr, nullptr);
    if (len <= 0) return std::string();
    std::string str;
    str.resize(len - 1);
    ::WideCharToMultiByte(CP_ACP, 0,
        wstr.c_str(), -1,
        &str[0], len,
        nullptr, nullptr);
    return str;
}
SourceCode/Bond/Servo/ToolUnits.h
@@ -60,5 +60,7 @@
        const char* fmt = "%Y-%m-%d %H:%M:%S");
    static std::string TimePointToLocalStringMs(const std::optional<TP>& tp);
    static std::string NowStrSec();
    static std::wstring AnsiToWString(const std::string& str);
    static std::string WStringToAnsi(const std::wstring& wstr);
};
SourceCode/Bond/Servo/resource.h
Binary files differ
SourceCode/Bond/Servo/stdafx.h
@@ -75,7 +75,7 @@
#include "..\RxWindows1.0\include\RxWindowsLib.h"
#include "..\HSMSSDK\Include\HSMSSDK.h"
#include "..\UserXLibrary\UserXAPI.h"
#ifdef _UNICODE
SourceCode/Bond/USERXLibrary/UserXAPI.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,136 @@
// UserX ç”¨æˆ·ç®¡ç†åº“(C æŽ¥å£ï¼‰/ UserX user management library (C API)
// è¦†ç›–功能 / Features: users, roles, actions, auth, logs(固定 SQLite3 æŒä¹…化 / SQLite3 persistence)
#pragma once
#if defined(_WIN32)
#  ifdef USERXLIBRARY_EXPORTS
#    define UX_API extern "C" __declspec(dllexport)
#  else
#    define UX_API extern "C" __declspec(dllimport)
#  endif
#else
#  define UX_API extern "C"
#endif
#include <wchar.h>
#ifdef _COMPILE_AS_LIB
#warning "compiling as lib!"
#else
#ifdef _DEBUG
#ifndef _WIN64
#pragma comment(lib, "../USERXLibrary/lib/Win32/Debug/UserXLibrary.lib")
#else
#pragma comment(lib, "../USERXLibrary/lib/x64/Debug/UserXLibrary.lib")
#endif
#else
#ifndef _WIN64
#pragma comment(lib, "../USERXLibrary/lib/Win32/Release/UserXLibrary.lib")
#else
#pragma comment(lib, "../USERXLibrary/lib/x64/Release/UserXLibrary.lib")
#endif
#endif
#endif // !BUILD_AS_LIB
// é¢å¤–错误码 / Extra error code
#ifndef UX_ERR_BAD_PASSWORD
#define UX_ERR_BAD_PASSWORD        -10    // å¯†ç é”™è¯¯ / bad password
#endif
// é”™è¯¯ç å®å®šä¹‰ / Error code macros
#define UX_OK                        0    // æˆåŠŸ / success
#define UX_ERR_INVALID_ARGS         -1    // å‚数错误 / invalid arguments
#define UX_ERR_NOT_FOUND            -2    // æœªæ‰¾åˆ° / not found
#define UX_ERR_EXISTS               -3    // å·²å­˜åœ¨ / already exists
#define UX_ERR_PERMISSION           -4    // æƒé™ä¸è¶³ / permission denied
#define UX_ERR_NOT_LOGGED_IN        -5    // æœªç™»å½• / not logged in
#define UX_ERR_BUFFER_TOO_SMALL     -6    // ç¼“冲区不足 / buffer too small
#define UX_ERR_DB                   -7    // I/O æˆ–数据库错误 / I/O or database error
#define UX_ERR_NOT_DEFINED          -8    // æœªå®šä¹‰ï¼ˆå¦‚动作不存在)/ not defined
#define UX_ERR_DB_NOT_EMPTY         -9    // æ•°æ®åº“非空(已初始化)/ database not empty
// è¿”回码 / Return codes(详见上方宏)
// UX_OK, UX_ERR_INVALID_ARGS, UX_ERR_NOT_FOUND, UX_ERR_EXISTS,
// UX_ERR_PERMISSION, UX_ERR_NOT_LOGGED_IN, UX_ERR_BUFFER_TOO_SMALL,
// UX_ERR_DB, UX_ERR_NOT_DEFINED, UX_ERR_DB_NOT_EMPTY
// åˆå§‹åŒ–:指定数据目录,内部将打开/创建 SQLite æ•°æ®åº“文件 UserX.db
// Init: provide data directory; opens/creates SQLite DB file UserX.db
UX_API int UX_Init(const wchar_t* storage_dir);
// å¯é€‰ï¼šè¦†ç›–数据库路径(仅使用第一个参数作为 .db æ–‡ä»¶è·¯å¾„,其他忽略)
// Optional: override DB path (use first arg as .db path; others ignored)
UX_API int UX_SetStorage(const wchar_t* users_db_path,
                         const wchar_t* /*unused_logs*/,
                         const wchar_t* /*unused_roles*/,
                         const wchar_t* /*unused_actions*/);
// é…ç½®è§’色(name:level æŒ‰è¡Œï¼‰/ Configure roles from lines "name:level"
// ä»…允许在“空数据库”(roles/users/actions/logs å››è¡¨å‡æ— æ•°æ®ï¼‰æ—¶è°ƒç”¨ï¼›å¦åˆ™è¿”回 -9。
// Only allowed when DB is empty (roles/users/actions/logs all zero rows); otherwise returns -9.
// e.g. L"Admin:100\nEngineer:50\nOperator:10\n"
UX_API int UX_SetRoleDefinitions(const wchar_t* roles_text);
// èŽ·å–è§’è‰²åˆ—è¡¨ï¼ˆname:level æŒ‰è¡Œï¼‰ï¼›buffer ä¸ºç©ºæˆ–不足时返回所需大小
// Get roles list as lines; returns required size if buffer is null/too small
UX_API int UX_GetRoles(wchar_t* buffer, int buffer_chars);
// ç”¨æˆ·ç®¡ç† / User management
UX_API int UX_AddUser(const wchar_t* username,
                      const wchar_t* display_name,
                      const wchar_t* password,
                      const wchar_t* role_name);
UX_API int UX_DeleteUser(const wchar_t* username);
// æ›´æ–°ä»»æ„å­—段(传 nullptr è¡¨ç¤ºä¿æŒä¸å˜ï¼‰/ Update subset; nullptr keeps field
UX_API int UX_UpdateUser(const wchar_t* username,
                         const wchar_t* new_display_name,
                         const wchar_t* new_password,
                         const wchar_t* new_role_name,
                         int enabled /* -1 keep, 0 disable, 1 enable */);
UX_API int UX_EnableUser(const wchar_t* username, int enabled /*0/1*/);
UX_API int UX_ResetPassword(const wchar_t* username, const wchar_t* new_password);
UX_API int UX_RenameUser(const wchar_t* username, const wchar_t* new_display_name);
// ç®¡ç†å‘˜æƒé™ï¼šåˆ é™¤æ‰€æœ‰ç”¨æˆ· / Admin-only
UX_API int UX_DeleteAllUsers();
// èŽ·å–ç”¨æˆ·åˆ—è¡¨ï¼ˆæ¯è¡Œï¼šusername,display,level,enabled)/ Query users
UX_API int UX_GetUsers(wchar_t* buffer, int buffer_chars);
// è®¤è¯ / Authentication
UX_API int UX_Login(const wchar_t* username, const wchar_t* password);
UX_API void UX_Logout();
UX_API int UX_IsLoggedIn();
UX_API void UX_Shutdown(); // é‡Šæ”¾èµ„源 / shutdown and free resources
UX_API int UX_GetCurrentUser(wchar_t* buffer, int buffer_chars);
// åŠ¨ä½œä¸Žæƒé™ / Actions & permissions
// å®šä¹‰/覆盖动作(名称、描述、最低角色)/ Define/overwrite action
UX_API int UX_DefineAction(const wchar_t* action_name,
                           const wchar_t* description,
                           const wchar_t* min_role_name);
// å¯æ‰§è¡Œè¿”回1,不可执行返回0;负数为错误 / 1 allowed, 0 denied
UX_API int UX_CanExecute(const wchar_t* action_name);
// è®°å½•动作到日志(时间、用户、动作、描述)/ Record action to logs
UX_API int UX_RecordAction(const wchar_t* action_name);
// èŽ·å–åŠ¨ä½œåˆ—è¡¨ï¼ˆæ¯è¡Œï¼šname,desc,minlevel)/ Get actions list
UX_API int UX_GetActions(wchar_t* buffer, int buffer_chars);
// è®°å½•尝试(允许/拒绝)/ record an attempt with allowed flag (1 allowed, 0 denied)
UX_API int UX_RecordAttempt(const wchar_t* action_name, int allowed /*1/0*/);
// æŸ¥è¯¢æœ€è¿‘ N æ¡æ—¥å¿—(每行:ISO8601时间,用户名,动作,描述)/ Query logs
UX_API int UX_QueryLogs(int last_n, wchar_t* buffer, int buffer_chars);
// æ ¹æ®é”™è¯¯ç è¿”回文字描述(中文/English)
// Return a human-readable message for an error code
UX_API const wchar_t* UX_ErrorMessage(int code);