Преглед на файлове

feat:增加建站,用户账户关联站点

Zhenghanjv преди 2 месеца
родител
ревизия
41aee9cf0a

+ 126 - 0
Platform/AI.Platform.Page/Pages/Site/Model/SiteModel.cs

@@ -0,0 +1,126 @@
+using AI.Platform.Core;
+using AI.Platform.Core.Entity.Site;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace AI.Platform.Page.Pages.Site.Model
+{
+    internal class SiteModel
+    {
+    }
+
+    /// <summary>
+    /// 站点列表显示数据
+    /// </summary>
+    public class SiteOutput
+    {
+        /// <summary>
+        /// id
+        /// </summary>
+        public virtual long Id { get; set; }
+
+        /// <summary>
+        /// 父站id
+        /// </summary>
+        public long ParentID { get; set; }
+
+        /// <summary>
+        /// 站名
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 父级站名
+        /// </summary>
+        public string ParentName { get; set; }
+
+        /// <summary>
+        /// 油站地址
+        /// </summary>
+        public string Address { get; set; }
+
+        /// <summary>
+        /// 联系方式
+        /// </summary>
+        public string Contact { get; set; }
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        public virtual DateTime? CreateTime { get; set; }
+    }
+
+    /// <summary>
+    /// 站点编辑弹框传递数据
+    /// </summary>
+    public class StateDialogModel
+    {
+        /// <summary>
+        /// 弹窗类型:1:新增;2:编辑;3:删除
+        /// </summary>
+        public int Type { get; set; }
+
+        /// <summary>
+        /// id
+        /// </summary>
+        public long? Id { get; set; }
+
+        /// <summary>
+        /// 站名
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 父级站点id
+        /// </summary>
+        public long ParentId { get; set; }
+
+        /// <summary>
+        /// 可供选择的父站
+        /// </summary>
+        public List<SiteInfo> Sites { get; set; }
+
+        /// <summary>
+        /// 油站地址
+        /// </summary>
+        public string Address { get; set; }
+
+        /// <summary>
+        /// 联系方式
+        /// </summary>
+        public string Contact { get; set; }
+
+        public SiteEntity ToCompany()
+        {
+            SiteEntity siteEntity = new SiteEntity()
+            {
+                ParentId = ParentId == 0 ? Global.CurrentUser.SiteId : ParentId,
+                Name = Name,
+                Address = Address,
+                Contact = Contact,
+                CreateTime = DateTime.Now
+            };
+
+            if (Id != null) siteEntity.Id = (long)this.Id;
+            return siteEntity;
+        }
+    }
+
+    /// <summary>
+    /// 用于记录站点信息,提供给用户信息编辑弹窗输送站点信息
+    /// </summary>
+    public class SiteInfo
+    {
+        /// <summary>
+        /// 站点id
+        /// </summary>
+        public long Id { get; set; }
+
+        /// <summary>
+        /// 站名
+        /// </summary>
+        public string Name { get; set; }
+    }
+}

+ 66 - 0
Platform/AI.Platform.Page/Pages/Site/Site.razor

@@ -0,0 +1,66 @@
+@page "/site/list"
+@using AI.Platform.Core.Entity.Site
+@using AI.Platform.Page.Pages.Site.Model
+@attribute [ReuseTabsPage(Title = "创建站点")]
+
+<Spin Spinning="Loading">
+    <div  class="filter_box">
+        <div class="filter_row" style="justify-content:start">
+            <span>站名</span><Input Placeholder="请输入要查找的站名" @bind-Value="filterData.siteName" Style="width:30%" />
+        </div>
+
+        <div class="filter_row" style="justify-content:start">
+            <Button Icon="search" OnClick="Query" Style="margin-right:2%">查询</Button>
+            <Button Icon="reload" OnClick="HandleReset" Style="margin-right:2%">重置</Button>
+            <Button Icon="upload" OnClick="@(() => ShowDialog(1, null))">新增</Button>
+        </div>
+    </div>
+
+    <UpdateSiteDialog onCallback="OnDialogCallback" onVisibleCallback="OnDialogVisibleCallback"  @bind-IsVisible="isOpen" @bind-model="model" /> 
+
+    <Table @ref="_Table" AutoHeight TItem="SiteOutput" DataSource="_DataSource" PageSize="filterData.pageSize" Total="Total"
+    OnChange="OnChange">
+        <ColumnDefinitions Context="row">
+            <PropertyColumn Property="c => c.Name" Title="站名" />
+            <PropertyColumn Property="c => c.Address" Title="地址" />
+            <PropertyColumn Property="c => c.ParentName" Title="归属集团" />
+            <PropertyColumn Property="c => c.Contact" Title="联系方式" />
+
+            <ActionColumn Width="180" Title="操作" Fixed="ColumnFixPlacement.Right">
+                <Button Type="ButtonType.Primary" Color="Color.Blue6" OnClick="@(() => ShowDialog(2, row))">编辑</Button>
+                <Button Type="ButtonType.Primary" Color="Color.Red6" OnClick="@(() => ShowDialog(3, row))">删除</Button>
+            </ActionColumn>
+        </ColumnDefinitions>
+
+        <PaginationTemplate>
+            <Pagination Class="@(context.PaginationClass + " my-custom-pagination")"
+                        Total="Total"
+                        PageSize="filterData.pageSize"
+                        Current="filterData.currentPage"
+                        ShowSizeChanger="true"
+                        ShowQuickJumper="true"
+                        OnChange="OnPageChange"
+                        ShowTotal="showTotal" />
+        </PaginationTemplate>
+    </Table>
+</Spin>
+
+<style>
+    .filter_box{
+        display:flex;
+        flex-direction:column;
+        align-items:center;
+        background:#ffffff;
+        padding:2%;
+    }
+
+    .filter_row{
+        display:flex;
+        flex-direction:row;
+        justify-content:space-between;
+        align-items:center;
+        width:100%;
+        margin-top:2%;
+    }
+
+</style>

+ 322 - 0
Platform/AI.Platform.Page/Pages/Site/Site.razor.cs

@@ -0,0 +1,322 @@
+using AI.Platform.Core;
+using AI.Platform.Core.Entity.Site;
+using AI.Platform.Page.Pages.Site.Model;
+using AntDesign;
+using AntDesign.TableModels;
+using Dm.util;
+using Microsoft.AspNetCore.Components;
+using Microsoft.JSInterop;
+using SqlSugar;
+using System.Data.Common;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace AI.Platform.Page.Pages.Site;
+
+public partial class Site
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    [Inject] NavigationManager NavigationManager { get; set; }
+    /// <summary>
+    /// 数据库仓储
+    /// </summary>
+    [Inject] SqlSugarRepository<SiteEntity> _Repository { get; set; }
+    /// <summary>
+    /// 
+    /// </summary>
+    [Inject] IJSRuntime IJSRuntime { get; set; }
+    /// <summary>
+    ///
+    /// </summary>
+    private ITable _Table;
+    
+    /// <summary>
+    /// 站点列表信息
+    /// </summary>
+    private List<SiteOutput> _DataSource;
+    
+    /// <summary>
+    /// 总条数
+    /// </summary>
+    private int Total = 0;
+    /// <summary>
+    /// 加载
+    /// </summary>
+    private bool Loading = false;
+
+    /// <summary>
+    /// 查询过滤条件
+    /// </summary>
+    private FilterData filterData { set; get; } = new();
+
+    /// <summary>
+    /// 编辑弹窗
+    /// </summary>
+    private UpdateSiteDialog updateMediaDialog { set; get; } = new();
+
+    /// <summary>
+    /// 标识弹窗是否显示
+    /// </summary>
+    private bool isOpen { set; get; } = false;
+
+    private StateDialogModel model = new StateDialogModel();
+
+    // <summary>
+    /// 站点
+    /// </summary>
+    private List<SiteInfo> Sites;
+
+    /// <summary>
+    /// 打开编辑弹窗
+    /// </summary>
+    /// <param name="type">类型:1:新增;2:编辑;3:删除</param>
+    /// <param name="media">广告信息</param>
+    /// <returns></returns>
+    private async Task ShowDialog(int type, SiteOutput? media)
+    {
+        model = new StateDialogModel();
+        model.Type = type;
+        model.Sites = Sites;
+        if(type == 2 || type == 3)
+        {
+            model.Address = media?.Address ?? "";
+            model.Name = media?.Name ?? "";
+            model.Contact = media?.Contact ?? "";
+            model.Id = media?.Id;
+        }
+
+        isOpen = true;
+    }
+    
+    /// <summary>
+    /// 编辑弹窗信息回调
+    /// </summary>
+    /// <param name="model"></param>
+    private async Task OnDialogCallback(StateDialogModel model)
+    {
+        switch (model.Type)
+        {
+            case 1:
+                await _Repository.InsertAsync(model.ToCompany());
+                break;
+            case 2:
+                SiteEntity siteEntity = model.ToCompany();
+                await _Repository.Context.Updateable(siteEntity).
+                    UpdateColumns(it => new { it.Name,it.Address, it.Contact})
+                    .ExecuteCommandAsync();
+                break;
+            case 3:
+                await _Repository.DeleteByIdAsync(model.Id);
+                break;
+        }
+        await Query();
+    }
+
+    /// <summary>
+    /// 编辑弹窗是否显示回调
+    /// </summary>
+    /// <param name="isOpen"></param>
+    private void OnDialogVisibleCallback(bool isOpen)
+    {
+        this.isOpen = isOpen;
+    }
+
+    /// <summary>
+    /// 表格查询
+    /// </summary>
+    /// <param name="query"></param>
+    /// <returns></returns>
+    private async Task OnChange(QueryModel<SiteOutput> query)
+        => await Query();
+
+    /// <summary>
+    /// 查
+    /// </summary>
+    /// <returns></returns>
+    private async Task Query()
+    {
+        Loading = true;
+        string? searchName = filterData.siteName;
+        int pageSize = filterData.pageSize;
+        int offset = (filterData.currentPage - 1) * pageSize;
+        string whereClause = string.IsNullOrEmpty(searchName)
+                    ? ""
+                     : "WHERE st.name LIKE CONCAT('%', @searchName, '%')";
+
+        if (Global.CurrentUser.Account != "admin")
+        {
+            long siteID = Global.CurrentUser.SiteId;
+            string countSql = $@"
+                    WITH RECURSIVE site_tree AS(
+                               SELECT id,parent_id
+                               FROM site_entity
+                               WHERE id = @siteID
+                               UNION ALL
+                               SELECT s.id,s.parent_id
+                               FROM site_entity s
+                               INNER JOIN site_tree t ON s.parent_id = t.id
+                    )
+                    SELECT 
+                        st.id  AS Id,st.parent_id AS ParentID,
+                        p.name AS ParentName
+                    FROM site_tree st
+                    LEFT JOIN site_entity p ON st.parent_id = p.id
+                    {whereClause};
+            ";
+            Total = await _Repository.Context.Ado.GetIntAsync(countSql, new { siteID, searchName });
+
+            string dataSql = $@"
+                    WITH RECURSIVE site_tree AS(
+                               SELECT id,parent_id,name,address,contact,create_time
+                               FROM site_entity
+                               WHERE id = @siteID
+                               UNION ALL
+                               SELECT s.id,s.parent_id,s.name,s.address,s.contact,s.create_time
+                               FROM site_entity s
+                               INNER JOIN site_tree t ON s.parent_id = t.id
+                    )
+                    SELECT 
+                        st.id AS Id,st.parent_id AS ParentID,st.name AS Name ,st.address AS Address,st.contact AS Contact,st.create_time AS CreateTime,
+                        p.name AS ParentName
+                    FROM site_tree st
+                    LEFT JOIN site_entity p ON st.parent_id = p.id
+                    {whereClause}
+                    ORDER BY st.create_time DESC
+                    LIMIT @pageSize OFFSET @offset;
+            ";
+            _DataSource = await _Repository.Context.Ado.SqlQueryAsync<SiteOutput>(dataSql, new { siteID, searchName, pageSize, offset });
+        } 
+        else
+        {
+            //,st.name,st.address,st.contact,st.create_time
+            string countSql = $@"
+                    SELECT 
+                        st.id,st.parent_id,
+                        p.name AS ParentName
+                    FROM site_entity st
+                    LEFT JOIN site_entity p ON st.parent_id = p.id
+                    {whereClause};
+            ";
+            Total = await _Repository.Context.Ado.GetIntAsync(countSql, new {  searchName });
+
+            string dataSql = $@"
+                    SELECT 
+                        st.id AS Id,st.parent_id AS ParentID,st.name AS Name ,st.address AS Address,st.contact AS Contact,st.create_time AS CreateTime,
+                        p.name AS ParentName
+                    FROM site_entity st
+                    LEFT JOIN site_entity p ON st.parent_id = p.id
+                    {whereClause}
+                    ORDER BY st.create_time DESC
+                    LIMIT @pageSize OFFSET @offset;
+            ";
+            _DataSource = await _Repository.Context.Ado.SqlQueryAsync<SiteOutput>(dataSql, new { searchName, pageSize, offset });
+        }
+
+            Loading = false;
+    }
+
+    Func<PaginationTotalContext, string> showTotal = ctx => $"总数 {ctx.Total} ";
+
+    private async Task OnPageChange(PaginationEventArgs args)
+    {
+        filterData.pageSize = args.PageSize;
+        filterData.currentPage = args.Page;
+        await Query();
+    }
+
+    /// <summary>
+    /// 清空查询条件
+    /// </summary>
+    private void HandleReset()
+    {
+
+    }
+
+    private async Task searchSitesAsync()
+    {
+        if (Global.CurrentUser.Account == "admin")
+        {
+            //string dataSql = $@"
+            //        SELECT 
+            //            st.id AS Id,st.name AS Name
+            //            p.name AS ParentName
+            //        FROM site_entity st
+            //        LEFT JOIN site_entity p ON st.parent_id = p.id;
+            //";
+            //Sites = await _Repository.Context.Ado.SqlQueryAsync<SiteInfo>(dataSql);
+
+            Sites = await _Repository.AsQueryable()
+                .Where(it => it.ParentId == 0)
+                .Select<SiteInfo>()
+                .ToListAsync();
+        }
+        else
+        {
+            //long siteID = Global.CurrentUser.SiteId;
+            //string dataSql = $@"
+            //        WITH RECURSIVE site_tree AS(
+            //                   SELECT id,parent_id,name,address,contact,create_time
+            //                   FROM site_entity
+            //                   WHERE id = @siteID
+            //                   UNION ALL
+            //                   SELECT s.id,s.parent_id,s.name,s.address,s.address,s.address
+            //                   FROM site_entity s
+            //                   INNER JOIN site_tree t ON s.parent_id = t.id
+            //        )
+            //        SELECT 
+            //            st.id AS Id,st.parent_id AS ParentID,st.name AS Name ,st.address AS Address,st.contact AS Contact,st.create_time AS CreateTime,
+            //            p.name AS ParentName
+            //        FROM site_tree st
+            //        LEFT JOIN site_entity p ON st.parent_id = p.id;
+            //";
+            //Sites = await _Repository.Context.Ado.SqlQueryAsync<SiteInfo>(dataSql, new { siteID });
+
+            Sites = await _Repository.AsQueryable()
+                .Where(it => it.Id == Global.CurrentUser.SiteId)
+                .Select<SiteInfo>()
+                .ToListAsync();
+        }
+    }
+
+    protected override async void OnInitialized()
+    {
+
+    }
+
+    protected override async Task OnAfterRenderAsync(bool firstRender)
+    {
+        if (firstRender)
+        {
+            await NavigationManager.RedirectLogin(IJSRuntime);
+            await Query();
+            await searchSitesAsync();
+        }
+    }
+
+    private async Task OnChange(QueryModel<SystemMenu> query)
+        => await Query();
+
+
+    /// <summary>
+    /// 查找条件
+    /// </summary>
+    public class FilterData
+    {
+        /// <summary>
+        /// 页码
+        /// </summary>
+        public int currentPage { set; get; } = 1;
+
+        /// <summary>
+        /// 页数
+        /// </summary>
+        public int pageSize { set; get; } = 10;
+
+        /// <summary>
+        /// 要查找的站名
+        /// </summary>
+        public string? siteName { set; get; }
+    }
+}

+ 92 - 0
Platform/AI.Platform.Page/Pages/Site/UpdateSiteDialog.razor

@@ -0,0 +1,92 @@
+@using AI.Platform.Page.Pages.Site.Model
+@using System.Threading.Tasks
+@using System.Text.Json
+
+@if (IsVisible)
+{
+    <div class="modal-overlay">
+        <div class="modal-content filter_box">
+            @if (Model?.Type == 1 || Model?.Type == 2)
+            {
+                <div class="filter_row">
+                    <span>站名</span><Input Placeholder="站名" @bind-Value="Model.Name" Style="width:30%" />
+
+                    <span>归属集团</span>
+                    <Select @bind-Value="Model.ParentId"
+                            Style="width:30%"
+                            TItemValue="long"
+                            TItem="string"
+                            DefaultActiveFirstOption="true" EnableSearch AllowClear>
+                        <SelectOptions>
+                            @foreach (var department in Model.Sites)
+                            {
+                                <SelectOption TItemValue="long" TItem="string" Value="@department.Id" Label="@department.Name" />
+                            }
+                        </SelectOptions>
+                    </Select>
+                    
+                </div>
+
+                <div class="filter_row">
+                    <span>联系方式</span><Input Placeholder="联系方式" @bind-Value="Model.Contact" Style="width:30%" />
+                    <span>地址</span><Input Placeholder="地址" @bind-Value="Model.Address" Style="width:30%" />
+                </div>
+            }
+
+            
+
+            @if (Model?.Type == 3)
+            {
+                <h3>是否删除站点?@(Model.Name)</h3>
+            }
+
+            <div class="filter_row" style="justify-content:end;margin-top:5%;">
+                <Button Icon="plus" OnClick="onSure" Style="margin-right:2%">确定</Button>
+                <Button Icon="reload" OnClick="Close" Style="margin-right:2%">取消</Button>
+            </div>
+        </div>
+
+    </div>
+}
+
+<style>
+    /* 遮罩层:全屏、半透明 */
+    .modal-overlay {
+        position: fixed;
+        top: 0;
+        left: 0;
+        width: 100vw;
+        height: 100vh;
+        background-color: rgba(0, 0, 0, 0.5); /* 半透明黑色遮罩 */
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        z-index: 1000;
+    }
+
+    /* 弹窗内容:白色卡片,居中由父容器控制 */
+    .modal-content {
+        background: white;
+        border-radius: 8px;
+        width: 80%;
+        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
+        /* 注意:不要设 height: 100vh,否则会拉满全屏 */
+    }
+
+    .filter_box {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        background: #ffffff;
+        padding: 2%;
+    }
+
+    .filter_row {
+        display: flex;
+        flex-direction: row;
+        justify-content: space-between;
+        align-items: center;
+        width: 100%;
+        margin-top: 2%;
+    }
+</style>

+ 91 - 0
Platform/AI.Platform.Page/Pages/Site/UpdateSiteDialog.razor.cs

@@ -0,0 +1,91 @@
+using AI.Platform.Core;
+using AI.Platform.Core.Entity.Site;
+using AI.Platform.Page.Pages.Site.Model;
+using Microsoft.AspNetCore.Components;
+
+namespace AI.Platform.Page.Pages.Site;
+
+public partial class UpdateSiteDialog
+{
+
+    /// <summary>
+    /// 信息回调
+    /// </summary>
+    [Parameter] public EventCallback<StateDialogModel> ModelChanged { get; set; }
+
+    /// <summary>
+    /// 打开/关闭窗口回调
+    /// </summary>
+    [Parameter] public EventCallback<bool> IsVisibleChanged { get; set; }
+
+    /// <summary>
+    /// 信息回调
+    /// </summary>
+    [Parameter] public EventCallback<StateDialogModel> onCallback { get; set; }
+
+    /// <summary>
+    /// 打开/关闭窗口回调
+    /// </summary>
+    [Parameter] public EventCallback<bool> onVisibleCallback { get; set; }
+
+    /// <summary>
+    /// 数据
+    /// </summary>
+    [Parameter] public StateDialogModel Model { get; set; }
+
+    /// <summary>
+    /// 配置是否弹窗
+    /// </summary>
+    [Parameter] public bool IsVisible { get; set; }
+
+    
+
+    protected override void OnParametersSet()
+    {
+        
+    }
+
+    /// <summary>
+    /// 关闭弹窗
+    /// </summary>
+    public async Task Close()
+    {
+        await OnlyClose();
+    }
+    /// <summary>
+    /// 仅关闭弹窗
+    /// </summary>
+    /// <returns></returns>
+    private async Task OnlyClose()
+    {
+        if (IsVisibleChanged.HasDelegate)
+        {
+            await IsVisibleChanged.InvokeAsync(false);
+        }
+
+        if (onVisibleCallback.HasDelegate)
+        {
+            await onVisibleCallback.InvokeAsync(false);
+        }
+    }
+
+    /// <summary>
+    /// 确定按钮事件
+    /// </summary>
+    /// <returns></returns>
+    private async Task onSure()
+    {
+        if (ModelChanged.HasDelegate)
+        {
+            await ModelChanged.InvokeAsync(Model);
+        }
+
+        if (onCallback.HasDelegate)
+        {
+            await onCallback.InvokeAsync(Model);
+        }
+        await OnlyClose();
+    }
+
+    
+}

+ 2 - 0
Platform/AI.Platform.Service/Common/SwaggerGroupAttribute.cs

@@ -25,6 +25,8 @@ public enum ApiGroupNames
     Bussiness,
     [GroupInfo(Title = "系统", Description = "系统相关接口")]
     System,
+    [GroupInfo(Title = "广告", Description = "广告相关接口")]
+    Media,
     [GroupInfo(Title = "统计", Description = "统计相关接口")]
     Statistics,
     [GroupInfo(Title = "测试", Description = "测试相关接口")]

+ 114 - 114
Platform/AI.Platform.Service/FileService.cs

@@ -1,118 +1,118 @@
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Http;
-using AI.Platform.Service.Common;
-using AI.Platform.Core.Entity;
-using Microsoft.AspNetCore.Components;
-using AI.Platform.Core;
-using static AI.Platform.Core.Entity.PublicEnum;
-using System.Net;
-using System;
-using AI.Platform.Service.Output;
-using Microsoft.AspNetCore.Http.HttpResults;
-
-namespace AI.Platform.Service;
-
-[ApiGroup(ApiGroupNames.System)]
-public class FileService : BaseService
-{
-    /// <summary>
-    /// 
-    /// </summary>
-    private readonly IHttpContextAccessor _contextAccessor;
-
-    public FileService(IHttpContextAccessor contextAccessor)
-    {
-        _contextAccessor = contextAccessor;
-    }
-
-    /// <summary>
-    /// 上传图片
-    /// </summary>
-    /// <param name="classification"></param>
-    /// <returns></returns>
-    [HttpPost, AllowAnonymous]
-    public async Task<object> UploadImage(string classification = "default")
-    {
-        var host = _contextAccessor.GetHost();
-        var file = _contextAccessor.HttpContext.Request.Form.Files[0];
-        if (file.Length <= 0)
-        {
-            return "";
-        }
-
-        using var memoryStream = new MemoryStream();
-        file.CopyTo(memoryStream);
-        var fileBytes = memoryStream.ToArray();
-
-        // 获取文件扩展名
-        var fileExtension = Path.GetExtension(file.FileName);
-        // 生成文件名
-        var id = Guid.NewGuid().ToString().ToUpper().Replace("-", "");
-        var fileName = $"{id}{fileExtension}";
-        // 获取当前日期
-        var currentDate = DateTime.Now;
-        // 构建目录路径
-        var directoryPath = Path.Combine("wwwroot", "image", classification, currentDate.ToString("yyyy"), currentDate.ToString("MM"), currentDate.ToString("dd"));
-        // 创建目录
-        if (!Directory.Exists(directoryPath))
-        {
-            Directory.CreateDirectory(directoryPath);
-        }
-        // 构建文件路径
-        var filePath = Path.Combine(directoryPath, fileName);
-        // 保存文件
-        await System.IO.File.WriteAllBytesAsync(filePath, fileBytes);
-
-        // 构建返回的相对路径
-        var relativePath = $"/image/{classification}/{currentDate:yyyy}/{currentDate:MM}/{currentDate:dd}/{fileName}";
-        return relativePath;
-    }
-
-    /// <summary>
-    /// 上传多媒体文件
-    /// </summary>
-    /// <param name="classification"></param>
-    /// <returns></returns>
-    [HttpPost, AllowAnonymous]
-    public async Task<MediaFileUploadOutput> UploadMedia(IFormFile file)
-    {
-       
-        if (file == null || file.Length <= 0)
-        {
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Http;
+using AI.Platform.Service.Common;
+using AI.Platform.Core.Entity;
+using Microsoft.AspNetCore.Components;
+using AI.Platform.Core;
+using static AI.Platform.Core.Entity.PublicEnum;
+using System.Net;
+using System;
+using AI.Platform.Service.Output;
+using Microsoft.AspNetCore.Http.HttpResults;
+
+namespace AI.Platform.Service;
+
+[ApiGroup(ApiGroupNames.System)]
+public class FileService : BaseService
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    private readonly IHttpContextAccessor _contextAccessor;
+
+    public FileService(IHttpContextAccessor contextAccessor)
+    {
+        _contextAccessor = contextAccessor;
+    }
+
+    /// <summary>
+    /// 上传图片
+    /// </summary>
+    /// <param name="classification"></param>
+    /// <returns></returns>
+    [HttpPost, AllowAnonymous]
+    public async Task<object> UploadImage(string classification = "default")
+    {
+        var host = _contextAccessor.GetHost();
+        var file = _contextAccessor.HttpContext.Request.Form.Files[0];
+        if (file.Length <= 0)
+        {
+            return "";
+        }
+
+        using var memoryStream = new MemoryStream();
+        file.CopyTo(memoryStream);
+        var fileBytes = memoryStream.ToArray();
+
+        // 获取文件扩展名
+        var fileExtension = Path.GetExtension(file.FileName);
+        // 生成文件名
+        var id = Guid.NewGuid().ToString().ToUpper().Replace("-", "");
+        var fileName = $"{id}{fileExtension}";
+        // 获取当前日期
+        var currentDate = DateTime.Now;
+        // 构建目录路径
+        var directoryPath = Path.Combine("wwwroot", "image", classification, currentDate.ToString("yyyy"), currentDate.ToString("MM"), currentDate.ToString("dd"));
+        // 创建目录
+        if (!Directory.Exists(directoryPath))
+        {
+            Directory.CreateDirectory(directoryPath);
+        }
+        // 构建文件路径
+        var filePath = Path.Combine(directoryPath, fileName);
+        // 保存文件
+        await System.IO.File.WriteAllBytesAsync(filePath, fileBytes);
+
+        // 构建返回的相对路径
+        var relativePath = $"/image/{classification}/{currentDate:yyyy}/{currentDate:MM}/{currentDate:dd}/{fileName}";
+        return relativePath;
+    }
+
+    /// <summary>
+    /// 上传多媒体文件
+    /// </summary>
+    /// <param name="classification"></param>
+    /// <returns></returns>
+    [HttpPost, AllowAnonymous]
+    public async Task<MediaFileUploadOutput> UploadMedia(IFormFile file)
+    {
+       
+        if (file == null || file.Length <= 0)
+        {
             return new MediaFileUploadOutput()
             {
                 isSuccess = false,
                 msg = "上传文件为空"
-            };
-        }
-
-        using var memoryStream = new MemoryStream();
-        file.CopyTo(memoryStream);
-        var fileBytes = memoryStream.ToArray();
-
-        // 获取文件扩展名
-        var fileExtension = Path.GetExtension(file.FileName);
-        // 生成文件名
-        var id = Guid.NewGuid().ToString().ToUpper().Replace("-", "");
-        var fileName = $"{id}_{file.FileName}";
-        // 获取当前日期
-        var currentDate = DateTime.Now;
-        // 构建目录路径
-        var directoryPath = Path.Combine("wwwroot", "madia", currentDate.ToString("MM"), currentDate.ToString("dd"));
-        // 创建目录
-        if (!Directory.Exists(directoryPath))
-        {
-            Directory.CreateDirectory(directoryPath);
-        }
-        // 构建文件路径
-        var filePath = Path.Combine(directoryPath, fileName);
-        // 保存文件
-        await System.IO.File.WriteAllBytesAsync(filePath, fileBytes);
-
-        //// 构建返回的相对路径
-        //var relativePath = $"/image/{currentDate:yyyy}/{currentDate:MM}/{currentDate:dd}/{fileName}";
-        //return relativePath;
+            };
+        }
+
+        using var memoryStream = new MemoryStream();
+        file.CopyTo(memoryStream);
+        var fileBytes = memoryStream.ToArray();
+
+        // 获取文件扩展名
+        var fileExtension = Path.GetExtension(file.FileName);
+        // 生成文件名
+        var id = Guid.NewGuid().ToString().ToUpper().Replace("-", "");
+        var fileName = $"{id}_{file.FileName}";
+        // 获取当前日期
+        var currentDate = DateTime.Now;
+        // 构建目录路径
+        var directoryPath = Path.Combine("wwwroot", "madia", currentDate.ToString("MM"), currentDate.ToString("dd"));
+        // 创建目录
+        if (!Directory.Exists(directoryPath))
+        {
+            Directory.CreateDirectory(directoryPath);
+        }
+        // 构建文件路径
+        var filePath = Path.Combine(directoryPath, fileName);
+        // 保存文件
+        await System.IO.File.WriteAllBytesAsync(filePath, fileBytes);
+
+        //// 构建返回的相对路径
+        //var relativePath = $"/image/{currentDate:yyyy}/{currentDate:MM}/{currentDate:dd}/{fileName}";
+        //return relativePath;
         return new MediaFileUploadOutput()
         {
             isSuccess = true,
@@ -120,6 +120,6 @@ public class FileService : BaseService
             fileName = file.FileName,
             guidName = fileName,
             savePath = filePath
-        };
-    }
-}
+        };
+    }
+}

+ 26 - 0
Platform/AI.Platform.Service/MediaService.cs

@@ -0,0 +1,26 @@
+using AI.Platform.Core;
+using AI.Platform.Core.Entity;
+using AI.Platform.Core.Entity.Media;
+using AI.Platform.Service.Common;
+using Microsoft.AspNetCore.Http;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace AI.Platform.Service;
+
+[ApiGroup(ApiGroupNames.Media)]
+public class MediaService
+{
+    private readonly SqlSugarRepository<MediaEntity> _mediaRepository;
+
+    private readonly IHttpContextAccessor _contextAccessor;
+
+    public MediaService(IHttpContextAccessor contextAccessor, SqlSugarRepository<MediaEntity> mediaRepository)
+    {
+        _contextAccessor = contextAccessor;
+        _mediaRepository = mediaRepository;
+    }
+
+    
+}

+ 17 - 0
Platform/AI.Platform.Tool/Entity/Device/ScreentEntity.cs

@@ -0,0 +1,17 @@
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace AI.Platform.Core.Entity.Device
+{
+    [SugarTable(null,"设备表")]
+    public class ScreentEntity: EntityBaseLite
+    {
+        [SugarColumn(ColumnDescription = "站点id")]
+        public long SiteID { get; set; }
+
+        [SugarColumn(ColumnDescription = "设备SN", IsNullable = false)]
+        public string SN { get; set; }
+    }
+}

+ 23 - 0
Platform/AI.Platform.Tool/Entity/Site/SiteEntity.cs

@@ -0,0 +1,23 @@
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace AI.Platform.Core.Entity.Site
+{
+    [SugarTable(null,"站点表")]
+    public class SiteEntity: EntityBaseLite
+    {
+        [SugarColumn(ColumnDescription ="父站id")]
+        public long ParentId { get; set; }
+
+        [SugarColumn(ColumnDescription = "站名",IsNullable = false)]
+        public string Name { get; set; }
+
+        [SugarColumn(ColumnDescription = "油站地址")]
+        public string Address { get; set; }
+
+        [SugarColumn(ColumnDescription = "联系方式")]
+        public string Contact {  get; set; }
+    }
+}

+ 3 - 0
Platform/AI.Platform.Tool/Entity/System/SystemMenu.cs

@@ -101,5 +101,8 @@ public class SystemMenuSeedData : ISeedData<SystemMenu>
 
              new SystemMenu() { Id = 21, ParentId = 0, Path="/media", Name="媒体管理", Key="media", Icon="appstore", Sort=1, Enabled=true, CreateTime = DateTime.Now },
             new SystemMenu() { Id = 22, ParentId = 21, Path="/media/list", Name="广告列表", Key="media.list", Icon="snippets", Sort=1, Enabled=true, CreateTime = DateTime.Now },
+
+             new SystemMenu() { Id = 23, ParentId = 0, Path="/site", Name="站点管理", Key="site", Icon="appstore", Sort=1, Enabled=true, CreateTime = DateTime.Now },
+            new SystemMenu() { Id = 24, ParentId = 23, Path="/site/list", Name="站点列表", Key="site.list", Icon="snippets", Sort=1, Enabled=true, CreateTime = DateTime.Now },
         ];
 }

+ 2 - 0
Platform/AI.Platform.Tool/Entity/System/SystemUser.cs

@@ -137,6 +137,8 @@ public class SystemUser: EntityBase
     [SugarColumn(IsIgnore = true)]
     public string Area { get; set; }
 
+    [SugarColumn(ColumnDescription = "站点id")]
+    public long SiteId { get; set; }
 }
 
 public class SystemUserSeedData : ISeedData<SystemUser>

+ 22 - 0
Platform/AI.Platform.Web/Components/Pages/Management/User/Model/UserModel.cs

@@ -0,0 +1,22 @@
+namespace AI.Platform.Web.Components.Pages.Management.User.Model
+{
+    public class UserModel
+    {
+    }
+
+    /// <summary>
+    /// 用于记录站点信息,提供给用户信息编辑弹窗输送站点信息
+    /// </summary>
+    public class SiteInfo
+    {
+        /// <summary>
+        /// 站点id
+        /// </summary>
+        public long Id {  get; set; }
+
+        /// <summary>
+        /// 站名
+        /// </summary>
+        public string Name { get; set; }
+    }
+}

+ 13 - 0
Platform/AI.Platform.Web/Components/Pages/Management/User/User.razor

@@ -144,6 +144,19 @@
                 </SelectOptions>
             </Select>
         </FormItem>
+        <FormItem Label="站点" Required>
+            <Select @bind-Value="@data.SiteId"
+                    TItemValue="long"
+                    TItem="string"
+                    DefaultActiveFirstOption="true" EnableSearch AllowClear>
+                <SelectOptions>
+                    @foreach (var department in Sites)
+                    {
+                        <SelectOption TItemValue="long" TItem="string" Value="@department.Id" Label="@department.Name" />
+                    }
+                </SelectOptions>
+            </Select>
+        </FormItem>
         <FormItem Label="是否启用" Required>
             <Select @bind-Value="@data.Enabled"
                     TItemValue="bool"

+ 69 - 3
Platform/AI.Platform.Web/Components/Pages/Management/User/User.razor.cs

@@ -1,9 +1,16 @@
-using AntDesign.TableModels;
-using AI.Platform.Core;
+using AI.Platform.Core;
+using AI.Platform.Core.Entity.Site;
 using AI.Platform.Core.Util;
+using AI.Platform.Page.Pages.Site;
+using AI.Platform.Page.Pages.Site.Model;
+using AI.Platform.Web.Components.Pages.Management.User.Model;
+using AntDesign;
+using AntDesign.TableModels;
 using Microsoft.AspNetCore.Components;
 using Microsoft.JSInterop;
 using SqlSugar;
+using System.Data.Common;
+using SiteInfo = AI.Platform.Web.Components.Pages.Management.User.Model.SiteInfo;
 
 namespace AI.Platform.Web.Components.Pages.Management.User;
 
@@ -24,6 +31,8 @@ public partial class User
             Roles = _role.GetList(x => x.Enabled == true);
 
             Departments = _department.GetList(x => x.Enabled == true);
+
+            await searchSitesAsync();
         }
     }
 
@@ -88,6 +97,51 @@ public partial class User
         Loading = false;
     }
 
+
+    private async Task searchSitesAsync()
+    {
+        if(Global.CurrentUser.Account == "admin")
+        {
+            //string dataSql = $@"
+            //        SELECT 
+            //            st.id AS Id,st.parent_id AS ParentID,st.name AS Name ,st.address AS Address,st.contact AS Contact,st.create_time AS CreateTime,
+            //            p.name AS ParentName
+            //        FROM site_entity st
+            //        LEFT JOIN site_entity p ON st.parent_id = p.id;
+            //";
+            //Sites = await _SiteRepository.Context.Ado.SqlQueryAsync<SiteInfo>(dataSql);
+            Sites = await _SiteRepository.AsQueryable()
+                .Select<SiteInfo>()
+                .ToListAsync();
+        } else
+        {
+            //long siteID = Global.CurrentUser.SiteId;
+            //string dataSql = $@"
+            //        WITH RECURSIVE site_tree AS(
+            //                   SELECT id,parent_id,name,address,contact,create_time
+            //                   FROM site_entity
+            //                   WHERE id = @siteID
+            //                   UNION ALL
+            //                   SELECT s.id,s.parent_id,s.name,s.address,s.address,s.address
+            //                   FROM site_entity s
+            //                   INNER JOIN site_tree t ON s.parent_id = t.id
+            //        )
+            //        SELECT 
+            //            st.id AS Id,st.parent_id AS ParentID,st.name AS Name ,st.address AS Address,st.contact AS Contact,st.create_time AS CreateTime,
+            //            p.name AS ParentName
+            //        FROM site_tree st
+            //        LEFT JOIN site_entity p ON st.parent_id = p.id;
+            //";
+            //Sites = await _repository.Context.Ado.SqlQueryAsync<SiteInfo>(dataSql, new { siteID });
+
+            Sites = await _SiteRepository.AsQueryable()
+                .Where(it => it.ParentId == Global.CurrentUser.SiteId || it.Id == Global.CurrentUser.SiteId)
+                .Select<SiteInfo>()
+                .ToListAsync();
+        }
+        
+    }
+
     /// <summary>
     /// 增/改
     /// </summary>
@@ -108,7 +162,7 @@ public partial class User
                 MessageService.Error("该账号名已存在");
                 return false;
             }
-
+            data.Password = Crypto.MD5Encrypt(data.Password);
             return await _repository.AsInsertable(data).ExecuteCommandAsync() > 0;
         }
         else
@@ -129,6 +183,7 @@ public partial class User
                 .SetColumns(x => x.Enabled == data.Enabled)
                 .SetColumns(x => x.Signature == data.Signature)
                 .SetColumns(x => x.AreaIds == AreadIds)
+                .SetColumns(x => x.SiteId == data.SiteId)
                 .Where(x => x.Id == data.Id)
                 .ExecuteCommandAsync() > 0;
             return res;
@@ -200,6 +255,11 @@ public partial class User
     /// 注入实例
     /// </summary>
     [Inject] private SqlSugarRepository<SystemUser> _repository { get; set; }
+
+    /// <summary>
+    /// 站点信息
+    /// </summary>
+    [Inject] SqlSugarRepository<SiteEntity> _SiteRepository { get; set; }
     /// <summary>
     /// 
     /// </summary>
@@ -264,6 +324,12 @@ public partial class User
     ///
     /// </summary>
     private List<SystemDepartment> Departments;
+
+    /// <summary>
+    /// 站点
+    /// </summary>
+    private List<SiteInfo> Sites;
+
     /// <summary>
     ///
     /// </summary>