From 957e20a06898c91fffe36bd2df59dfeffc63903f Mon Sep 17 00:00:00 2001 From: 李曜臣 Date: Tue, 28 Apr 2026 20:46:50 +0800 Subject: [PATCH] 门店支持 --- 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportCreateInputVo.cs | 13 +++++++++++++ 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportGetOutputDto.cs | 17 +++++++++++++++++ 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportUpdateInputVo.cs | 13 +++++++++++++ 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILocationSupportAppService.cs | 29 +++++++++++++++++++++++++++++ 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLocationSupportDbEntity.cs | 29 +++++++++++++++++++++++++++++ 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LocationSupportAppService.cs | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/scripts/fl_location_support_create.sql | 16 ++++++++++++++++ 项目相关文档/美国版App登录接口说明.md | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 415 insertions(+), 0 deletions(-) create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportCreateInputVo.cs create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportGetOutputDto.cs create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportUpdateInputVo.cs create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILocationSupportAppService.cs create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLocationSupportDbEntity.cs create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LocationSupportAppService.cs create mode 100644 美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/scripts/fl_location_support_create.sql diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportCreateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportCreateInputVo.cs new file mode 100644 index 0000000..c3b02c6 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportCreateInputVo.cs @@ -0,0 +1,13 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LocationSupport; + +/// +/// 新增门店 Support 联系方式 +/// +public class LocationSupportCreateInputVo +{ + public string LocationId { get; set; } = string.Empty; + + public string SupportPhone { get; set; } = string.Empty; + + public string SupportEmail { get; set; } = string.Empty; +} diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportGetOutputDto.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportGetOutputDto.cs new file mode 100644 index 0000000..e30ed66 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportGetOutputDto.cs @@ -0,0 +1,17 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LocationSupport; + +/// +/// 门店 Support 联系方式详情 +/// +public class LocationSupportGetOutputDto +{ + public string Id { get; set; } = string.Empty; + + public string LocationId { get; set; } = string.Empty; + + public string? LocationName { get; set; } + + public string SupportPhone { get; set; } = string.Empty; + + public string SupportEmail { get; set; } = string.Empty; +} diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportUpdateInputVo.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportUpdateInputVo.cs new file mode 100644 index 0000000..2c131f3 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportUpdateInputVo.cs @@ -0,0 +1,13 @@ +namespace FoodLabeling.Application.Contracts.Dtos.LocationSupport; + +/// +/// 编辑门店 Support 联系方式 +/// +public class LocationSupportUpdateInputVo +{ + public string LocationId { get; set; } = string.Empty; + + public string SupportPhone { get; set; } = string.Empty; + + public string SupportEmail { get; set; } = string.Empty; +} diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILocationSupportAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILocationSupportAppService.cs new file mode 100644 index 0000000..4b403ad --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILocationSupportAppService.cs @@ -0,0 +1,29 @@ +using FoodLabeling.Application.Contracts.Dtos.LocationSupport; +using Volo.Abp.Application.Services; + +namespace FoodLabeling.Application.Contracts.IServices; + +/// +/// 门店 Support 联系方式管理(后台设置) +/// +public interface ILocationSupportAppService : IApplicationService +{ + /// + /// 按门店查询 Support 联系方式 + /// + /// 门店Id + Task GetByLocationIdAsync(string locationId); + + /// + /// 新增门店 Support 联系方式(每个门店仅允许一条) + /// + /// 联系方式 + Task CreateAsync(LocationSupportCreateInputVo input); + + /// + /// 编辑门店 Support 联系方式 + /// + /// 联系方式主键 + /// 联系方式 + Task UpdateAsync(string id, LocationSupportUpdateInputVo input); +} diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLocationSupportDbEntity.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLocationSupportDbEntity.cs new file mode 100644 index 0000000..296d2f5 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLocationSupportDbEntity.cs @@ -0,0 +1,29 @@ +using SqlSugar; + +namespace FoodLabeling.Application.Services.DbModels; + +/// +/// 门店 Support 联系方式(每个门店仅一条,对 App Support 页展示) +/// +[SugarTable("fl_location_support")] +public class FlLocationSupportDbEntity +{ + [SugarColumn(IsPrimaryKey = true)] + public string Id { get; set; } = string.Empty; + + public bool IsDeleted { get; set; } + + public DateTime CreationTime { get; set; } + + public string? CreatorId { get; set; } + + public string? LastModifierId { get; set; } + + public DateTime? LastModificationTime { get; set; } + + public string LocationId { get; set; } = string.Empty; + + public string SupportPhone { get; set; } = string.Empty; + + public string SupportEmail { get; set; } = string.Empty; +} diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LocationSupportAppService.cs b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LocationSupportAppService.cs new file mode 100644 index 0000000..e1aff20 --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LocationSupportAppService.cs @@ -0,0 +1,208 @@ +using FoodLabeling.Application.Contracts.Dtos.LocationSupport; +using FoodLabeling.Application.Contracts.IServices; +using FoodLabeling.Application.Services.DbModels; +using FoodLabeling.Domain.Entities; +using Volo.Abp; +using Volo.Abp.Application.Services; +using Volo.Abp.Guids; +using Volo.Abp.Uow; +using Yi.Framework.SqlSugarCore.Abstractions; + +namespace FoodLabeling.Application.Services; + +/// +/// 门店 Support 联系方式(后台设置,App 展示) +/// +public class LocationSupportAppService : ApplicationService, ILocationSupportAppService +{ + private readonly ISqlSugarDbContext _dbContext; + private readonly IGuidGenerator _guidGenerator; + + public LocationSupportAppService(ISqlSugarDbContext dbContext, IGuidGenerator guidGenerator) + { + _dbContext = dbContext; + _guidGenerator = guidGenerator; + } + + /// + public async Task GetByLocationIdAsync(string locationId) + { + var lid = locationId?.Trim(); + if (string.IsNullOrWhiteSpace(lid)) + { + throw new UserFriendlyException("门店Id不能为空"); + } + + var entity = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => !x.IsDeleted && x.LocationId == lid); + if (entity is null) + { + return null; + } + + return await MapOutputAsync(entity); + } + + /// + [UnitOfWork] + public async Task CreateAsync(LocationSupportCreateInputVo input) + { + if (input is null) + { + throw new UserFriendlyException("入参不能为空"); + } + + var locationId = NormalizeLocationId(input.LocationId); + var phone = NormalizeRequired(input.SupportPhone, "Support 电话不能为空"); + var email = NormalizeRequired(input.SupportEmail, "Support 邮箱不能为空"); + EnsureEmailFormat(email); + + await EnsureLocationExistsAsync(locationId); + + var existed = await _dbContext.SqlSugarClient.Queryable() + .AnyAsync(x => !x.IsDeleted && x.LocationId == locationId); + if (existed) + { + throw new UserFriendlyException("该门店已配置 Support 联系方式,请使用编辑接口"); + } + + var now = Clock.Now; + var entity = new FlLocationSupportDbEntity + { + Id = _guidGenerator.Create().ToString(), + IsDeleted = false, + CreationTime = now, + CreatorId = CurrentUser?.Id?.ToString(), + LastModificationTime = now, + LastModifierId = CurrentUser?.Id?.ToString(), + LocationId = locationId, + SupportPhone = phone, + SupportEmail = email + }; + + await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync(); + return (await MapOutputAsync(entity))!; + } + + /// + [UnitOfWork] + public async Task UpdateAsync(string id, LocationSupportUpdateInputVo input) + { + var supportId = id?.Trim(); + if (string.IsNullOrWhiteSpace(supportId)) + { + throw new UserFriendlyException("联系方式Id不能为空"); + } + + if (input is null) + { + throw new UserFriendlyException("入参不能为空"); + } + + var entity = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => !x.IsDeleted && x.Id == supportId); + if (entity is null) + { + throw new UserFriendlyException("联系方式记录不存在"); + } + + var locationId = NormalizeLocationId(input.LocationId); + var phone = NormalizeRequired(input.SupportPhone, "Support 电话不能为空"); + var email = NormalizeRequired(input.SupportEmail, "Support 邮箱不能为空"); + EnsureEmailFormat(email); + await EnsureLocationExistsAsync(locationId); + + var conflict = await _dbContext.SqlSugarClient.Queryable() + .AnyAsync(x => !x.IsDeleted && x.LocationId == locationId && x.Id != supportId); + if (conflict) + { + throw new UserFriendlyException("目标门店已配置 Support 联系方式,一个门店仅允许一条"); + } + + entity.LocationId = locationId; + entity.SupportPhone = phone; + entity.SupportEmail = email; + entity.LastModificationTime = Clock.Now; + entity.LastModifierId = CurrentUser?.Id?.ToString(); + + await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync(); + return (await MapOutputAsync(entity))!; + } + + private string NormalizeLocationId(string? locationId) + { + var lid = locationId?.Trim(); + if (string.IsNullOrWhiteSpace(lid)) + { + throw new UserFriendlyException("门店Id不能为空"); + } + + if (!Guid.TryParse(lid, out _)) + { + throw new UserFriendlyException("门店Id格式不正确"); + } + + return lid; + } + + private static string NormalizeRequired(string? value, string message) + { + var normalized = value?.Trim(); + if (string.IsNullOrWhiteSpace(normalized)) + { + throw new UserFriendlyException(message); + } + + return normalized; + } + + private static void EnsureEmailFormat(string email) + { + if (!email.Contains("@", StringComparison.Ordinal) || email.StartsWith("@", StringComparison.Ordinal) || + email.EndsWith("@", StringComparison.Ordinal)) + { + throw new UserFriendlyException("Support 邮箱格式不正确"); + } + } + + private async Task EnsureLocationExistsAsync(string locationId) + { + if (!Guid.TryParse(locationId, out var gid)) + { + throw new UserFriendlyException("门店Id格式不正确"); + } + + var exists = await _dbContext.SqlSugarClient.Queryable() + .AnyAsync(x => !x.IsDeleted && x.Id == gid); + if (!exists) + { + throw new UserFriendlyException("门店不存在"); + } + } + + private async Task MapOutputAsync(FlLocationSupportDbEntity? entity) + { + if (entity is null) + { + return null; + } + + string? locationName = null; + if (Guid.TryParse(entity.LocationId, out var lid)) + { + var loc = await _dbContext.SqlSugarClient.Queryable() + .FirstAsync(x => !x.IsDeleted && x.Id == lid); + locationName = loc?.LocationName?.Trim(); + } + + return new LocationSupportGetOutputDto + { + Id = entity.Id, + LocationId = entity.LocationId, + LocationName = locationName, + SupportPhone = entity.SupportPhone, + SupportEmail = entity.SupportEmail + }; + } +} + diff --git a/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/scripts/fl_location_support_create.sql b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/scripts/fl_location_support_create.sql new file mode 100644 index 0000000..4d16b3c --- /dev/null +++ b/美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/scripts/fl_location_support_create.sql @@ -0,0 +1,16 @@ +-- 门店 Support 联系方式(每个门店仅一条) +CREATE TABLE IF NOT EXISTS `fl_location_support` ( + `Id` varchar(50) NOT NULL COMMENT '主键', + `IsDeleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除', + `CreationTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `CreatorId` varchar(50) DEFAULT NULL COMMENT '创建人', + `LastModifierId` varchar(50) DEFAULT NULL COMMENT '最后修改人', + `LastModificationTime` datetime DEFAULT NULL COMMENT '最后修改时间', + `LocationId` varchar(50) NOT NULL COMMENT '门店Id(对应location.Id)', + `SupportPhone` varchar(100) NOT NULL COMMENT 'Support 电话', + `SupportEmail` varchar(200) NOT NULL COMMENT 'Support 邮箱', + PRIMARY KEY (`Id`), + UNIQUE KEY `uk_fl_location_support_locationid` (`LocationId`), + KEY `idx_fl_location_support_isdeleted` (`IsDeleted`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='门店 Support 联系方式'; + diff --git a/项目相关文档/美国版App登录接口说明.md b/项目相关文档/美国版App登录接口说明.md index 1e57ff8..15c7d3e 100644 --- a/项目相关文档/美国版App登录接口说明.md +++ b/项目相关文档/美国版App登录接口说明.md @@ -285,6 +285,96 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... --- +## 接口 6:门店 Support 联系方式(App 展示) + +用于 App「Support」页面读取当前门店的联系方式(电话、邮箱)。 +后台由 `LocationSupportAppService` 维护,规则是 **一个门店仅允许一条** 联系方式记录。 + +### HTTP + +- **方法**:`GET` +- **路径**:`/api/app/location-support/by-location-id?locationId={locationId}`(以 Swagger 中 `LocationSupport` 为准) +- **鉴权**:需要登录(`Authorization: Bearer {token}`) + +### 请求参数 + +| 参数名 | 位置 | 类型 | 必填 | 说明 | +|--------|------|------|------|------| +| `locationId` | Query | string | 是 | 门店主键(Guid 字符串) | + +### 响应体(LocationSupportGetOutputDto) + +| 字段(JSON) | 类型 | 说明 | +|--------------|------|------| +| `id` | string | 联系方式主键 | +| `locationId` | string | 门店主键 | +| `locationName` | string \| null | 门店名称 | +| `supportPhone` | string | Support 电话 | +| `supportEmail` | string | Support 邮箱 | + +> 若门店尚未配置联系方式,接口返回 `null`。 + +### 响应示例 + +```json +{ + "id": "3a2f4fda-1a93-4a35-9b98-95dca7bb5d2a", + "locationId": "a2696b9e-2277-11f1-b4c6-00163e0c7c4f", + "locationName": "Downtown Store", + "supportPhone": "1-800-SUPPORT", + "supportEmail": "support@medvantage.com" +} +``` + +--- + +## 后台维护接口:Location Support(新增/编辑) + +仅后台管理端使用,用于配置 App Support 页面展示内容。 + +### 接口 A:新增门店联系方式 + +- **方法**:`POST` +- **路径**:`/api/app/location-support` +- **Content-Type**:`application/json` + +请求体(LocationSupportCreateInputVo): + +```json +{ + "locationId": "a2696b9e-2277-11f1-b4c6-00163e0c7c4f", + "supportPhone": "1-800-SUPPORT", + "supportEmail": "support@medvantage.com" +} +``` + +约束: +- 同一 `locationId` 只能新增一条,重复会报错:`该门店已配置 Support 联系方式,请使用编辑接口` + +### 接口 B:编辑门店联系方式 + +- **方法**:`PUT` +- **路径**:`/api/app/location-support/{id}` +- **Content-Type**:`application/json` + +请求体(LocationSupportUpdateInputVo)与新增一致: + +```json +{ + "locationId": "a2696b9e-2277-11f1-b4c6-00163e0c7c4f", + "supportPhone": "1-800-SUPPORT", + "supportEmail": "support@medvantage.com" +} +``` + +常见错误: +- `门店Id不能为空` / `门店Id格式不正确` +- `Support 电话不能为空` +- `Support 邮箱不能为空` / `Support 邮箱格式不正确` +- `目标门店已配置 Support 联系方式,一个门店仅允许一条` + +--- + ## 与其他登录方式的区别 | 场景 | 说明 | -- libgit2 0.21.4