using NCC.Common.Core.Manager; using NCC.Common.Enum; using NCC.Common.Extension; using NCC.Common.Filter; using NCC.Dependency; using NCC.DynamicApiController; using NCC.FriendlyException; using NCC.Extend.Interfaces.LqYcsdJsj; using Mapster; using Microsoft.AspNetCore.Mvc; using SqlSugar; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading.Tasks; using NCC.Extend.Entitys.lq_ycsd_jsj; using NCC.Extend.Entitys.lq_jinsanjiao_user; using NCC.Extend.Entitys.lq_mdxx; using NCC.Extend.Entitys.lq_kd_jksyj; using NCC.Extend.Entitys.Dto.LqYcsdJsj; using Yitter.IdGenerator; using NCC.Common.Helper; using NCC.JsonSerialization; using NCC.Common.Model.NPOI; using NCC.Common.Configuration; using NCC.DataEncryption; using NCC.ClayObject; namespace NCC.Extend.LqYcsdJsj { /// /// 金三角设定服务 /// [ApiDescriptionSettings(Tag = "绿纤金三角服务", Name = "LqYcsdJsj", Order = 200)] [Route("api/Extend/[controller]")] public class LqYcsdJsjService : ILqYcsdJsjService, IDynamicApiController, ITransient { private readonly ISqlSugarRepository _lqYcsdJsjRepository; private readonly SqlSugarScope _db; private readonly IUserManager _userManager; /// /// 初始化一个类型的新实例 /// public LqYcsdJsjService(ISqlSugarRepository lqYcsdJsjRepository,IUserManager userManager) { _lqYcsdJsjRepository = lqYcsdJsjRepository; _db = _lqYcsdJsjRepository.Context; _userManager = userManager; } #region 获取金三角设定 /// /// 获取金三角设定 /// /// 参数 /// [HttpGet("{id}")] public async Task GetInfo(string id) { var entity = await _db.Queryable().FirstAsync(p => p.Id == id); var output = entity.Adapt(); return output; } #endregion #region 获取金三角设定详情 /// /// 获取金三角设定详情(包含成员信息) /// /// 金三角ID /// [HttpGet("{id}/detail")] public async Task GetDetail(string id) { var entity = await _db.Queryable().FirstAsync(p => p.Id == id); if (entity == null) throw NCCException.Oh(ErrorCode.COM1005); var output = entity.Adapt(); // 获取成员信息 var members = await _db.Queryable() .Where(x => x.JsjId == id && x.Status == "ACTIVE") .OrderBy(x => x.SortOrder) .Select(x => new { id = x.Id, userId = x.UserId, userName = x.UserName, isLeader = x.IsLeader, sortOrder = x.SortOrder, status = x.Status }) .ToListAsync(); return new { jsj = output, members = members }; } #endregion #region 获取金三角设定列表 /// /// 获取金三角设定列表 /// /// 请求参数 /// [HttpGet("")] public async Task GetList([FromQuery] LqYcsdJsjListQueryInput input) { var sidx = input.sidx == null ? "id" : input.sidx; var data = await _db.Queryable() .WhereIF(!string.IsNullOrEmpty(input.id), p => p.Id.Contains(input.id)) .WhereIF(!string.IsNullOrEmpty(input.yf), p => p.Yf.Contains(input.yf)) .WhereIF(!string.IsNullOrEmpty(input.md), p => p.Md.Contains(input.md)) .WhereIF(!string.IsNullOrEmpty(input.jsj), p => p.Jsj.Contains(input.jsj)) .Select(it => new LqYcsdJsjListOutput { id = it.Id, yf = it.Yf, md = it.Md, jsj = it.Jsj }).MergeTable().OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize); return PageResult.SqlSugarPageResult(data); } #endregion #region 获取本月金三角列表 /// /// 获取本月金三角列表 /// /// 本月金三角列表 [HttpGet("Actions/GetThisMonthList")] public async Task GetThisMonthList() { var data = await _db.Queryable() .Where(x => x.Yf == DateTime.Now.ToString("yyyyMM")) .Select(it => new LqYcsdJsjListOutput { id = it.Id, yf = it.Yf, md = it.Md, jsj = it.Jsj }).MergeTable().ToListAsync(); return data; } #endregion #region 获取用户本月金三角信息 /// /// 获取某个用户本月所在的金三角信息 /// /// 用户ID /// 用户本月所在的金三角信息 [HttpGet("Actions/GetThisMonthJsjInfo")] public async Task GetThisMonthJsjInfo([FromQuery] string userId) { if (string.IsNullOrEmpty(userId)) { throw NCCException.Oh(ErrorCode.COM1001, "用户ID不能为空"); } var currentMonth = DateTime.Now.ToString("yyyyMM"); // 先查询用户的金三角关联信息 var jsjUser = await _db.Queryable().Where(x => x.UserId == userId && x.DeleteMark == 0).FirstAsync(); if (jsjUser == null) { return null; } // 查询金三角信息 var jsj = await _db.Queryable().Where(x => x.Id == jsjUser.JsjId && x.Yf == currentMonth).FirstAsync(); if (jsj == null) { return null; } // 查询门店信息 var store = await _db.Queryable() .Where(x => x.Id == jsj.Md).FirstAsync(); return new { jsjId = jsj.Id, jsjName = jsj.Jsj, month = jsj.Yf, storeId = jsj.Md, storeName = store?.Dm, userName = jsjUser.UserName, isLeader = jsjUser.IsLeader, status = jsjUser.Status, sortOrder = jsjUser.SortOrder }; } #endregion #region 新建金三角 /// /// 新建金三角 /// /// 参数 /// [HttpPost("")] public async Task Create([FromBody] LqYcsdJsjCrInput input) { var userInfo = await _userManager.GetUserInfo(); // 参数验证 if (string.IsNullOrEmpty(input.yf)) throw NCCException.Oh(ErrorCode.COM1000, "月份不能为空"); if (string.IsNullOrEmpty(input.md)) throw NCCException.Oh(ErrorCode.COM1000, "门店不能为空"); if (string.IsNullOrEmpty(input.jsj)) throw NCCException.Oh(ErrorCode.COM1000, "金三角名称不能为空"); // 验证月份格式 if (!DateTime.TryParseExact(input.yf, "yyyyMM", null, DateTimeStyles.None, out _)) { throw NCCException.Oh(ErrorCode.COM1000, "月份格式必须为yyyyMM"); } // 验证多人战队必须有顾问 if (input.members != null && input.members.Count >= 2) { var hasLeader = input.members.Any(m => m.isLeader == 1); if (!hasLeader) { throw NCCException.Oh(ErrorCode.COM1000, "两人或两人以上的战队必须有一个顾问"); } } // 验证金三角名称是否已存在 var existingJsj = await _db.Queryable() .Where(x => x.Yf == input.yf && x.Md == input.md && x.Jsj == input.jsj) .FirstAsync(); if (existingJsj != null) { throw NCCException.Oh(ErrorCode.COM1000, "该门店该月份已存在同名金三角"); } try { // 开启事务 _db.BeginTran(); // 1. 创建门店T区(如果不存在) await CreateOrUpdateStoreTArea(input.yf, input.md, userInfo.userId); // 2. 创建金三角基础信息 var entity = input.Adapt(); entity.Id = YitIdHelper.NextId().ToString(); var isOk = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteCommandAsync(); if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000); // 3. 创建金三角成员绑定关系 if (input.members != null && input.members.Count > 0) { await CreateJsjMembers(entity.Id, input.members, userInfo.userId); } // 4. 创建站点战队T区(如果金三角不是T区且不是单人) if (!input.jsj.EndsWith("T区") && input.members != null && input.members.Count > 1) { await CreateOrUpdateTeamTArea(input.yf, input.jsj, input.md, userInfo.userId); } // 提交事务 _db.CommitTran(); } catch (Exception ex) { // 回滚事务 _db.RollbackTran(); throw NCCException.Oh(ErrorCode.COM1000, ex.Message); } } #endregion #region 获取金三角设定无分页列表 /// /// 获取金三角设定无分页列表 /// /// 请求参数 /// [NonAction] public async Task GetNoPagingList([FromQuery] LqYcsdJsjListQueryInput input) { var sidx = input.sidx == null ? "id" : input.sidx; var data = await _db.Queryable() .WhereIF(!string.IsNullOrEmpty(input.id), p => p.Id.Contains(input.id)) .WhereIF(!string.IsNullOrEmpty(input.yf), p => p.Yf.Contains(input.yf)) .WhereIF(!string.IsNullOrEmpty(input.md), p => p.Md.Contains(input.md)) .WhereIF(!string.IsNullOrEmpty(input.jsj), p => p.Jsj.Contains(input.jsj)) .Select(it => new LqYcsdJsjListOutput { id = it.Id, yf = it.Yf, md = it.Md, jsj = it.Jsj }).MergeTable().OrderBy(sidx + " " + input.sort).ToListAsync(); return data; } #endregion #region 导出金三角设定 /// /// 导出金三角设定 /// /// 请求参数 /// [HttpGet("Actions/Export")] public async Task Export([FromQuery] LqYcsdJsjListQueryInput input) { var userInfo = await _userManager.GetUserInfo(); var exportData = new List(); if (input.dataType == 0) { var data = Clay.Object(await this.GetList(input)); exportData = data.Solidify>().list; } else { exportData = await this.GetNoPagingList(input); } List paramList = "[{\"value\":\"主键\",\"field\":\"id\"},{\"value\":\"月份\",\"field\":\"yf\"},{\"value\":\"门店\",\"field\":\"md\"},{\"value\":\"金三角\",\"field\":\"jsj\"}]".ToList(); ExcelConfig excelconfig = new ExcelConfig(); excelconfig.FileName = "金三角设定.xls"; excelconfig.HeadFont = "微软雅黑"; excelconfig.HeadPoint = 10; excelconfig.IsAllSizeColumn = true; excelconfig.ColumnModel = new List(); List selectKeyList = input.selectKey.Split(',').ToList(); foreach (var item in selectKeyList) { var isExist = paramList.Find(p => p.field == item); if (isExist != null) { excelconfig.ColumnModel.Add(new ExcelColumnModel() { Column = isExist.field, ExcelColumn = isExist.value }); } } var addPath = FileVariable.TemporaryFilePath + excelconfig.FileName; ExcelExportHelper.Export(exportData, excelconfig, addPath); var fileName = _userManager.UserId + "|" + addPath + "|xls"; var output = new { name = excelconfig.FileName, url = "/api/File/Download?encryption=" + DESCEncryption.Encrypt(fileName, "NCC") }; return output; } #endregion #region 批量删除金三角设定 /// /// 批量删除金三角设定 /// /// 主键数组 /// [HttpPost("batchRemove")] public async Task BatchRemove([FromBody] List ids) { var entitys = await _db.Queryable().In(it => it.Id, ids).ToListAsync(); if (entitys.Count > 0) { try { //开启事务 _db.BeginTran(); //批量删除金三角设定 await _db.Deleteable().In(d => d.Id, ids).ExecuteCommandAsync(); //关闭事务 _db.CommitTran(); } catch (Exception) { //回滚事务 _db.RollbackTran(); throw NCCException.Oh(ErrorCode.COM1002); } } } #endregion #region 更新金三角设定 /// /// 更新金三角设定 /// /// 主键 /// 参数 /// [HttpPut("{id}")] public async Task Update(string id, [FromBody] LqYcsdJsjUpInput input) { var entity = input.Adapt(); var isOk = await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1001); } #endregion #region 删除金三角设定 /// /// 删除金三角设定 /// /// [HttpDelete("{id}")] public async Task Delete(string id) { var entity = await _db.Queryable().FirstAsync(p => p.Id == id); _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); var isOk = await _db.Deleteable().Where(d => d.Id == id).ExecuteCommandAsync(); if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1002); } #endregion #region 金三角月度业绩统计 /// /// 获取金三角月度业绩统计 /// /// 月份(格式:yyyyMM),为空则查询所有月份 /// 金三角ID,为空则查询所有金三角 /// 门店ID,为空则查询所有门店 /// 金三角月度业绩统计列表 [HttpGet("Actions/GetMonthlyPerformance")] public async Task GetMonthlyPerformance([FromQuery] string month = null, [FromQuery] string jsjId = null, [FromQuery] string storeId = null) { var query = _db.Queryable() .LeftJoin((jsj, jksyj) => jsj.Id == jksyj.Jsj_id) .LeftJoin((jsj, jksyj, md) => jsj.Md == md.Id) .Where((jsj, jksyj, md) => jsj.Yf != null) .Where((jsj, jksyj, md) => jksyj.Yjsj != null) .Where((jsj, jksyj, md) => jksyj.Jksyj != null && jksyj.Jksyj != "" && jksyj.Jksyj != "0") .WhereIF(!string.IsNullOrEmpty(month), (jsj, jksyj, md) => jsj.Yf == month) .WhereIF(!string.IsNullOrEmpty(jsjId), (jsj, jksyj, md) => jsj.Id == jsjId) .WhereIF(!string.IsNullOrEmpty(storeId), (jsj, jksyj, md) => jsj.Md == storeId) .GroupBy((jsj, jksyj, md) => new { jsj.Id, jsj.Jsj, jsj.Yf, jsj.Md, md.Dm }) .Select((jsj, jksyj, md) => new { jsjId = jsj.Id, jsjName = jsj.Jsj, month = jsj.Yf, storeId = jsj.Md, storeName = md.Dm, totalPerformance = SqlFunc.AggregateSum(SqlFunc.ToDecimal(jksyj.Jksyj)), orderCount = SqlFunc.AggregateCount(jksyj.Id), avgPerformance = SqlFunc.AggregateAvg(SqlFunc.ToDecimal(jksyj.Jksyj)), lastOrderDate = SqlFunc.AggregateMax(jksyj.Yjsj), firstOrderDate = SqlFunc.AggregateMin(jksyj.Yjsj) }) .OrderBy(jsj => jsj.month, OrderByType.Desc) .OrderBy(jsj => jsj.totalPerformance, OrderByType.Desc); var result = await query.ToListAsync(); return result; } /// /// 获取金三角月度业绩排名 /// /// 月份(格式:yyyyMM) /// 排名数量,默认10 /// 金三角月度业绩排名列表 [HttpGet("Actions/GetMonthlyPerformanceRanking")] public async Task GetMonthlyPerformanceRanking([FromQuery] string month, [FromQuery] int topCount = 10) { if (string.IsNullOrEmpty(month)) { throw NCCException.Oh(ErrorCode.COM1001, "月份参数不能为空"); } var query = _db.Queryable() .LeftJoin((jsj, jksyj) => jsj.Id == jksyj.Jsj_id) .LeftJoin((jsj, jksyj, md) => jsj.Md == md.Id) .Where((jsj, jksyj, md) => jsj.Yf == month) .Where((jsj, jksyj, md) => jksyj.Yjsj != null) .Where((jsj, jksyj, md) => jksyj.Jksyj != null && jksyj.Jksyj != "" && jksyj.Jksyj != "0") .GroupBy((jsj, jksyj, md) => new { jsj.Id, jsj.Jsj, jsj.Yf, jsj.Md, md.Dm }) .Select((jsj, jksyj, md) => new { jsjId = jsj.Id, jsjName = jsj.Jsj, month = jsj.Yf, storeId = jsj.Md, storeName = md.Dm, totalPerformance = SqlFunc.AggregateSum(SqlFunc.ToDecimal(jksyj.Jksyj)), orderCount = SqlFunc.AggregateCount(jksyj.Id), ranking = 0 // 排名将在后续计算 }) .OrderBy(jsj => jsj.totalPerformance, OrderByType.Desc) .Take(topCount); var result = await query.ToListAsync(); // 创建包含排名的结果 var rankedResult = result.Select((item, index) => new { item.jsjId, item.jsjName, item.month, item.storeId, item.storeName, item.totalPerformance, item.orderCount, ranking = index + 1 }).ToList(); return rankedResult; } #endregion #region 私有辅助方法 /// /// 创建或更新门店T区 /// /// 月份 /// 门店ID /// 创建人ID /// private async Task CreateOrUpdateStoreTArea(string yf, string mdId, string creatorUserId) { // 获取门店名称 var storeName = await GetStoreNameById(mdId); var tAreaName = $"{storeName}T区"; // 检查该门店该月份是否已有T区 var existingTArea = await _db.Queryable() .Where(x => x.Yf == yf && x.Md == mdId && x.Jsj == tAreaName) .FirstAsync(); if (existingTArea == null) { // 创建门店T区 var tAreaEntity = new LqYcsdJsjEntity { Id = YitIdHelper.NextId().ToString(), Yf = yf, Md = mdId, Jsj = tAreaName }; await _db.Insertable(tAreaEntity).ExecuteCommandAsync(); } } /// /// 创建或更新战队T区 /// /// 月份 /// 战队名称 /// 门店ID /// 创建人ID /// private async Task CreateOrUpdateTeamTArea(string yf, string teamName, string mdId, string creatorUserId) { var tAreaName = $"{teamName}T区"; // 检查该战队该月份是否已有T区 var existingTArea = await _db.Queryable() .Where(x => x.Yf == yf && x.Md == mdId && x.Jsj == tAreaName) .FirstAsync(); if (existingTArea == null) { // 创建战队T区 var tAreaEntity = new LqYcsdJsjEntity { Id = YitIdHelper.NextId().ToString(), Yf = yf, Md = mdId, Jsj = tAreaName }; await _db.Insertable(tAreaEntity).ExecuteCommandAsync(); } } /// /// 创建金三角成员绑定关系 /// /// 金三角ID /// 成员列表 /// 创建人ID /// private async Task CreateJsjMembers(string jsjId, List members, string creatorUserId) { var memberEntities = new List(); for (int i = 0; i < members.Count; i++) { var member = members[i]; var memberEntity = new LqJinsanjiaoUserEntity { Id = YitIdHelper.NextId().ToString(), JsjId = jsjId, UserId = member.userId, UserName = member.userName, IsLeader = member.isLeader, Status = "ACTIVE", SortOrder = member.sortOrder > 0 ? member.sortOrder : i + 1, CreatorTime = DateTime.Now, CreatorUserId = creatorUserId, DeleteMark = 0, Month = DateTime.Now.ToString("yyyyMM") }; memberEntities.Add(memberEntity); } if (memberEntities.Count > 0) { await _db.Insertable(memberEntities).ExecuteCommandAsync(); } } /// /// 根据门店ID获取门店名称 /// /// 门店ID /// private async Task GetStoreNameById(string mdId) { try { var storeName = await _db.Queryable() .Where(x => x.Id == mdId) .Select(x => x.Dm) .FirstAsync(); return string.IsNullOrEmpty(storeName) ? "未知门店" : storeName; } catch { return "未知门店"; } } #endregion } }