All checks were successful
continuous-integration/drone/push Build is passing
- Add marker pole CRUD functionality with database tables (odf_marker_poles, odf_marker_pole_images) - Implement marker pole API endpoints and service layer with data isolation by department - Add UniApp pages for marker pole list, detail, and creation workflows - Add Vue management backend pages for marker pole and audit log management - Implement audit logging system via ActionFilter to track all business entity modifications - Extend search API to include marker poles in results alongside cables and faults - Add OdfAuditLogsController and service for querying audit trail data - Update optical box detail page with left-right frame color scheme (green-orange) - Add cable type page as entry point for marker poles and fault lists - Create database migration scripts for v1.2.0 schema and permissions - Add DeptDataScopeHelper for department-based data access control - Update MCP settings to disable SQL Server connection and fix formatting - Add marker pole service integration in UniApp with COS image upload support
431 lines
17 KiB
C#
431 lines
17 KiB
C#
using Lazy.Captcha.Core;
|
||
|
||
using Microsoft.AspNetCore.Mvc;
|
||
|
||
using ZR.Model.Models;
|
||
using ZR.Model.System;
|
||
using ZR.Model.System.Dto;
|
||
using ZR.ServiceCore.Model.Dto;
|
||
|
||
namespace ZR.Admin.WebApi.Controllers.System
|
||
{
|
||
/// <summary>
|
||
/// 登录
|
||
/// </summary>
|
||
[ApiExplorerSettings(GroupName = "sys")]
|
||
public class SysLoginController : BaseController
|
||
{
|
||
private readonly ISysUserService sysUserService;
|
||
private readonly ISysMenuService sysMenuService;
|
||
private readonly ISysLoginService sysLoginService;
|
||
private readonly ISysPermissionService permissionService;
|
||
private readonly ICaptcha SecurityCodeHelper;
|
||
private readonly ISysConfigService sysConfigService;
|
||
private readonly ISysRoleService roleService;
|
||
private readonly ISmsCodeLogService smsCodeLogService;
|
||
private readonly ISysDeptService deptService;
|
||
|
||
public SysLoginController(
|
||
ISysMenuService sysMenuService,
|
||
ISysUserService sysUserService,
|
||
ISysLoginService sysLoginService,
|
||
ISysPermissionService permissionService,
|
||
ISysConfigService configService,
|
||
ISysRoleService sysRoleService,
|
||
ISmsCodeLogService smsCodeLogService,
|
||
ISysDeptService deptService,
|
||
ICaptcha captcha)
|
||
{
|
||
SecurityCodeHelper = captcha;
|
||
this.sysMenuService = sysMenuService;
|
||
this.sysUserService = sysUserService;
|
||
this.sysLoginService = sysLoginService;
|
||
this.permissionService = permissionService;
|
||
this.sysConfigService = configService;
|
||
this.smsCodeLogService = smsCodeLogService;
|
||
this.deptService = deptService;
|
||
roleService = sysRoleService;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 登录
|
||
/// </summary>
|
||
/// <param name="loginBody">登录对象</param>
|
||
/// <returns></returns>
|
||
[Route("login")]
|
||
[HttpPost]
|
||
[Log(Title = "登录")]
|
||
[AllowAnonymous]
|
||
public IActionResult Login([FromBody] LoginBodyDto loginBody)
|
||
{
|
||
if (loginBody == null) { throw new CustomException("请求参数错误"); }
|
||
loginBody.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext);
|
||
SysConfig sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff");
|
||
if (sysConfig?.ConfigValue != "off" && !SecurityCodeHelper.Validate(loginBody.Uuid, loginBody.Code))
|
||
{
|
||
return ToResponse(ResultCode.CAPTCHA_ERROR, "验证码错误");
|
||
}
|
||
|
||
sysLoginService.CheckLockUser(loginBody.Username);
|
||
string location = HttpContextExtension.GetIpInfo(loginBody.LoginIP);
|
||
var user = sysLoginService.Login(loginBody, new SysLogininfor() { LoginLocation = location });
|
||
|
||
List<SysRole> roles = roleService.SelectUserRoleListByUserId(user.UserId);
|
||
|
||
// 分公司账号登录管理后台限制:非超级管理员的分公司账号必须拥有「分公司管理员」角色
|
||
if (!user.IsAdmin && !roles.Any(r => r.RoleKey == GlobalConstant.AdminRole))
|
||
{
|
||
var dept = deptService.GetFirst(d => d.DeptId == user.DeptId);
|
||
if (dept != null)
|
||
{
|
||
// 判断是否为分公司级别:Ancestors 中包含两级以上(如 "0,100" 表示分公司)
|
||
var ancestors = dept.Ancestors?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
|
||
bool isBranchCompany = ancestors.Length >= 2;
|
||
if (isBranchCompany && !roles.Any(r => r.RoleKey == "branch_admin"))
|
||
{
|
||
throw new CustomException(ResultCode.LOGIN_ERROR, "该账号无权登录管理后台,请联系管理员分配「分公司管理员」角色", false);
|
||
}
|
||
}
|
||
}
|
||
|
||
//权限集合 eg *:*:*,system:user:list
|
||
List<string> permissions = permissionService.GetMenuPermission(new SysUserDto() { UserId = user.UserId });
|
||
|
||
TokenModel loginUser = new(user.Adapt<TokenModel>(), roles.Adapt<List<Roles>>())
|
||
{
|
||
TenantId = loginBody.TenantId,
|
||
Permissions = permissions,
|
||
};
|
||
//CacheService.SetUserPerms(GlobalConstant.UserPermKEY + user.UserId, permissions);
|
||
return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser)));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 登录
|
||
/// </summary>
|
||
/// <param name="loginBody">登录对象</param>
|
||
/// <returns></returns>
|
||
[Route("appLogin")]
|
||
[HttpPost]
|
||
[Log(Title = "登录")]
|
||
[AllowAnonymous]
|
||
public IActionResult AppLogin([FromBody] LoginBodyDto loginBody)
|
||
{
|
||
if (loginBody == null) { throw new CustomException("请求参数错误"); }
|
||
loginBody.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext);
|
||
|
||
sysLoginService.CheckLockUser(loginBody.Username);
|
||
string location = HttpContextExtension.GetIpInfo(loginBody.LoginIP);
|
||
var user = sysLoginService.Login(loginBody, new SysLogininfor() { LoginLocation = location });
|
||
|
||
List<SysRole> roles = roleService.SelectUserRoleListByUserId(user.UserId);
|
||
//权限集合 eg *:*:*,system:user:list
|
||
List<string> permissions = permissionService.GetMenuPermission(new SysUserDto() { UserId = user.UserId });
|
||
|
||
TokenModel loginUser = new(user.Adapt<TokenModel>(), roles.Adapt<List<Roles>>())
|
||
{
|
||
TenantId = loginBody.TenantId,
|
||
Permissions = permissions,
|
||
};
|
||
//CacheService.SetUserPerms(GlobalConstant.UserPermKEY + user.UserId, permissions);
|
||
var jwt = JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser));
|
||
return SUCCESS(new { jwt, user.UserId, user.UserName, permissions });
|
||
}
|
||
//
|
||
|
||
/// <summary>
|
||
/// 注销
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[Log(Title = "注销")]
|
||
[HttpPost("logout")]
|
||
[AllowAnonymous]
|
||
public IActionResult LogOut()
|
||
{
|
||
var userid = HttpContext.GetUId();
|
||
var name = HttpContext.GetName();
|
||
|
||
CacheService.RemoveUserPerms(GlobalConstant.UserPermKEY + userid);
|
||
return SUCCESS(new { name, id = userid });
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取用户信息
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpGet("getInfo")]
|
||
public IActionResult GetUserInfo()
|
||
{
|
||
long userId = HttpContext.GetUId();
|
||
var user = sysUserService.SelectUserById(userId);
|
||
|
||
//前端校验按钮权限使用
|
||
//角色集合 eg: admin,yunying,common
|
||
List<string> roles = permissionService.GetRolePermission(user);
|
||
//权限集合 eg *:*:*,system:user:list
|
||
List<string> permissions = permissionService.GetMenuPermission(user);
|
||
user.WelcomeContent = GlobalConstant.WelcomeMessages[new Random().Next(0, GlobalConstant.WelcomeMessages.Length)];
|
||
user.Password = string.Empty;
|
||
CacheService.SetUserPerms(GlobalConstant.UserPermKEY + userId, permissions);
|
||
return SUCCESS(new { user = user.Adapt<SysUserDto>(), roles, permissions });
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取路由信息
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpGet("getRouters")]
|
||
public IActionResult GetRouters()
|
||
{
|
||
long uid = HttpContext.GetUId();
|
||
var menus = sysMenuService.SelectMenuTreeByUserId(uid);
|
||
|
||
return SUCCESS(sysMenuService.BuildMenus(menus));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取路由信息
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpGet("getAppRouters")]
|
||
public IActionResult GetAppRouters(int v = 0)
|
||
{
|
||
long uid = HttpContext.GetUId();
|
||
var perms = permissionService.GetMenuPermission(new SysUserDto() { UserId = uid });
|
||
|
||
return SUCCESS(sysMenuService.GetAppMenus(perms, v));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成图片验证码
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpGet("captchaImage")]
|
||
[AllowAnonymous]
|
||
public IActionResult CaptchaImage()
|
||
{
|
||
string uuid = Guid.NewGuid().ToString().Replace("-", "");
|
||
|
||
SysConfig sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff");
|
||
var captchaOff = sysConfig?.ConfigValue ?? "0";
|
||
var info = SecurityCodeHelper.Generate(uuid, 60);
|
||
var obj = new { captchaOff, uuid, img = info.Base64 };// File(stream, "image/png")
|
||
|
||
return SUCCESS(obj);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 注册
|
||
/// </summary>
|
||
/// <param name="dto"></param>
|
||
/// <returns></returns>
|
||
[HttpPost("/register")]
|
||
[AllowAnonymous]
|
||
[Log(Title = "注册", BusinessType = BusinessType.INSERT)]
|
||
public IActionResult Register([FromBody] RegisterDto dto)
|
||
{
|
||
SysConfig config = sysConfigService.GetSysConfigByKey("sys.account.register");
|
||
if (config?.ConfigValue != "true")
|
||
{
|
||
return ToResponse(ResultCode.CUSTOM_ERROR, "当前系统没有开启注册功能!");
|
||
}
|
||
SysConfig sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff");
|
||
if (sysConfig?.ConfigValue != "off" && !SecurityCodeHelper.Validate(dto.Uuid, dto.Code))
|
||
{
|
||
return ToResponse(ResultCode.CAPTCHA_ERROR, "验证码错误");
|
||
}
|
||
dto.UserIP = HttpContext.GetClientUserIp();
|
||
SysUser user = sysUserService.Register(dto);
|
||
if (user.UserId > 0)
|
||
{
|
||
return SUCCESS(user);
|
||
}
|
||
return ToResponse(ResultCode.CUSTOM_ERROR, "注册失败,请联系管理员");
|
||
}
|
||
|
||
#region 二维码登录
|
||
|
||
/// <summary>
|
||
/// 生成二维码
|
||
/// </summary>
|
||
/// <param name="uuid"></param>
|
||
/// <param name="deviceId"></param>
|
||
/// <returns></returns>
|
||
[HttpGet("/GenerateQrcode")]
|
||
[AllowAnonymous]
|
||
public IActionResult GenerateQrcode(string uuid, string deviceId)
|
||
{
|
||
var state = Guid.NewGuid().ToString();
|
||
var dict = new Dictionary<string, object>
|
||
{
|
||
{ "state", state }
|
||
};
|
||
CacheService.SetScanLogin(uuid, dict);
|
||
return SUCCESS(new
|
||
{
|
||
status = 1,
|
||
state,
|
||
uuid,
|
||
codeContent = new { uuid, deviceId }// "https://qm.qq.com/cgi-bin/qm/qr?k=kgt4HsckdljU0VM-0kxND6d_igmfuPlL&authKey=r55YUbruiKQ5iwC/folG7KLCmZ++Y4rQVgNlvLbUniUMkbk24Y9+zNuOmOnjAjRc&noverify=0"
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 轮询判断扫码状态
|
||
/// </summary>
|
||
/// <param name="dto"></param>
|
||
/// <returns></returns>
|
||
[HttpPost("/VerifyScan")]
|
||
[AllowAnonymous]
|
||
public IActionResult VerifyScan([FromBody] ScanDto dto)
|
||
{
|
||
int status = -1;
|
||
object token = string.Empty;
|
||
if (CacheService.GetScanLogin(dto.Uuid) is Dictionary<string, object> str)
|
||
{
|
||
status = 0;
|
||
str.TryGetValue("token", out token);
|
||
if (str.ContainsKey("status") && (string)str.GetValueOrDefault("status") == "success")
|
||
{
|
||
status = 2;//扫码成功
|
||
CacheService.RemoveScanLogin(dto.Uuid);
|
||
}
|
||
}
|
||
|
||
return SUCCESS(new { status, token });
|
||
}
|
||
|
||
/// <summary>
|
||
/// 移动端扫码登录
|
||
/// </summary>
|
||
/// <param name="dto"></param>
|
||
/// <returns></returns>
|
||
[HttpPost("/ScanLogin")]
|
||
[Log(Title = "扫码登录")]
|
||
public IActionResult ScanLogin([FromBody] ScanDto dto)
|
||
{
|
||
if (dto == null) { return ToResponse(ResultCode.CUSTOM_ERROR, "扫码失败"); }
|
||
var name = App.HttpContext.GetName();
|
||
|
||
sysLoginService.CheckLockUser(name);
|
||
|
||
TokenModel tokenModel = JwtUtil.GetLoginUser(HttpContext);
|
||
if (CacheService.GetScanLogin(dto.Uuid) is not null)
|
||
{
|
||
Dictionary<string, object> dict = new() { };
|
||
dict.Add("status", "success");
|
||
dict.Add("token", JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(tokenModel)));
|
||
CacheService.SetScanLogin(dto.Uuid, dict);
|
||
|
||
return SUCCESS(1);
|
||
}
|
||
return ToResponse(ResultCode.FAIL, "二维码已失效");
|
||
}
|
||
#endregion
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="dto"></param>
|
||
/// <returns></returns>
|
||
[HttpPost("/checkMobile")]
|
||
[Log(Title = "发送短息", BusinessType = BusinessType.INSERT)]
|
||
[AllowAnonymous]
|
||
public IActionResult CheckMobile([FromBody] PhoneLoginDto dto)
|
||
{
|
||
dto.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext);
|
||
var uid = HttpContext.GetUId();
|
||
//SysConfig sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff");
|
||
//if (!SecurityCodeHelper.Validate(dto.Uuid, dto.Code, false))
|
||
//{
|
||
// return ToResponse(ResultCode.CUSTOM_ERROR, "验证码错误");
|
||
//}
|
||
if (dto.SendType == 0)
|
||
{
|
||
var info = sysUserService.GetFirst(f => f.Phonenumber == dto.PhoneNum) ?? throw new CustomException(ResultCode.CUSTOM_ERROR, "该手机号不存在", false);
|
||
uid = info.UserId;
|
||
}
|
||
if (dto.SendType == 1)
|
||
{
|
||
if (sysUserService.CheckPhoneBind(dto.PhoneNum).Count > 0)
|
||
{
|
||
return ToResponse(ResultCode.CUSTOM_ERROR, "手机号已绑定其他账号");
|
||
}
|
||
}
|
||
|
||
string location = HttpContextExtension.GetIpInfo(dto.LoginIP);
|
||
|
||
smsCodeLogService.AddSmscodeLog(new SmsCodeLog()
|
||
{
|
||
Userid = uid,
|
||
PhoneNum = dto.PhoneNum.ParseToLong(),
|
||
SendType = dto.SendType,
|
||
UserIP = dto.LoginIP,
|
||
Location = location,
|
||
});
|
||
|
||
return SUCCESS(1);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 手机号登录
|
||
/// </summary>
|
||
/// <param name="loginBody">登录对象</param>
|
||
/// <returns></returns>
|
||
[Route("PhoneLogin")]
|
||
[HttpPost]
|
||
[Log(Title = "手机号登录")]
|
||
[AllowAnonymous]
|
||
public IActionResult PhoneLogin([FromBody] PhoneLoginDto loginBody)
|
||
{
|
||
if (loginBody == null) { throw new CustomException("请求参数错误"); }
|
||
loginBody.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext);
|
||
|
||
if (!CacheService.CheckPhoneCode(loginBody.PhoneNum, loginBody.PhoneCode))
|
||
{
|
||
return ToResponse(ResultCode.CUSTOM_ERROR, "短信验证码错误");
|
||
}
|
||
var info = sysUserService.GetFirst(f => f.Phonenumber == loginBody.PhoneNum) ?? throw new CustomException(ResultCode.CUSTOM_ERROR, "该手机号不存在", false);
|
||
var infoModel = info.Adapt<SysUserDto>();
|
||
sysLoginService.CheckLockUser(info.UserName);
|
||
string location = HttpContextExtension.GetIpInfo(loginBody.LoginIP);
|
||
var user = sysLoginService.PhoneLogin(loginBody, new SysLogininfor() { LoginLocation = location }, infoModel);
|
||
|
||
List<SysRole> roles = roleService.SelectUserRoleListByUserId(user.UserId);
|
||
//权限集合 eg *:*:*,system:user:list
|
||
List<string> permissions = permissionService.GetMenuPermission(user);
|
||
|
||
TokenModel loginUser = new(user.Adapt<TokenModel>(), roles.Adapt<List<Roles>>())
|
||
{
|
||
TenantId = loginBody.TenantId,
|
||
Permissions = permissions,
|
||
};
|
||
//CacheService.SetUserPerms(GlobalConstant.UserPermKEY + user.UserId, permissions);
|
||
return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser)));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 手机号绑定
|
||
/// </summary>
|
||
/// <param name="loginBody"></param>
|
||
/// <returns></returns>
|
||
[Route("/PhoneBind")]
|
||
[HttpPost]
|
||
[Log(Title = "手机号绑定")]
|
||
[AllowAnonymous]
|
||
public IActionResult PhoneBind([FromBody] PhoneLoginDto loginBody)
|
||
{
|
||
if (loginBody == null) { throw new CustomException("请求参数错误"); }
|
||
loginBody.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext);
|
||
var uid = HttpContext.GetUId();
|
||
if (!CacheService.CheckPhoneCode(loginBody.PhoneNum, loginBody.PhoneCode))
|
||
{
|
||
return ToResponse(ResultCode.CUSTOM_ERROR, "短信验证码错误");
|
||
}
|
||
var result = sysUserService.ChangePhoneNum(uid, loginBody.PhoneNum);
|
||
|
||
return SUCCESS(result);
|
||
}
|
||
}
|
||
}
|