Commit 957e20a06898c91fffe36bd2df59dfeffc63903f

Authored by 李曜臣
1 parent 3ff23c20

门店支持

美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportCreateInputVo.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LocationSupport;
  2 +
  3 +/// <summary>
  4 +/// 新增门店 Support 联系方式
  5 +/// </summary>
  6 +public class LocationSupportCreateInputVo
  7 +{
  8 + public string LocationId { get; set; } = string.Empty;
  9 +
  10 + public string SupportPhone { get; set; } = string.Empty;
  11 +
  12 + public string SupportEmail { get; set; } = string.Empty;
  13 +}
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportGetOutputDto.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LocationSupport;
  2 +
  3 +/// <summary>
  4 +/// 门店 Support 联系方式详情
  5 +/// </summary>
  6 +public class LocationSupportGetOutputDto
  7 +{
  8 + public string Id { get; set; } = string.Empty;
  9 +
  10 + public string LocationId { get; set; } = string.Empty;
  11 +
  12 + public string? LocationName { get; set; }
  13 +
  14 + public string SupportPhone { get; set; } = string.Empty;
  15 +
  16 + public string SupportEmail { get; set; } = string.Empty;
  17 +}
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/Dtos/LocationSupport/LocationSupportUpdateInputVo.cs 0 → 100644
  1 +namespace FoodLabeling.Application.Contracts.Dtos.LocationSupport;
  2 +
  3 +/// <summary>
  4 +/// 编辑门店 Support 联系方式
  5 +/// </summary>
  6 +public class LocationSupportUpdateInputVo
  7 +{
  8 + public string LocationId { get; set; } = string.Empty;
  9 +
  10 + public string SupportPhone { get; set; } = string.Empty;
  11 +
  12 + public string SupportEmail { get; set; } = string.Empty;
  13 +}
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application.Contracts/IServices/ILocationSupportAppService.cs 0 → 100644
  1 +using FoodLabeling.Application.Contracts.Dtos.LocationSupport;
  2 +using Volo.Abp.Application.Services;
  3 +
  4 +namespace FoodLabeling.Application.Contracts.IServices;
  5 +
  6 +/// <summary>
  7 +/// 门店 Support 联系方式管理(后台设置)
  8 +/// </summary>
  9 +public interface ILocationSupportAppService : IApplicationService
  10 +{
  11 + /// <summary>
  12 + /// 按门店查询 Support 联系方式
  13 + /// </summary>
  14 + /// <param name="locationId">门店Id</param>
  15 + Task<LocationSupportGetOutputDto?> GetByLocationIdAsync(string locationId);
  16 +
  17 + /// <summary>
  18 + /// 新增门店 Support 联系方式(每个门店仅允许一条)
  19 + /// </summary>
  20 + /// <param name="input">联系方式</param>
  21 + Task<LocationSupportGetOutputDto> CreateAsync(LocationSupportCreateInputVo input);
  22 +
  23 + /// <summary>
  24 + /// 编辑门店 Support 联系方式
  25 + /// </summary>
  26 + /// <param name="id">联系方式主键</param>
  27 + /// <param name="input">联系方式</param>
  28 + Task<LocationSupportGetOutputDto> UpdateAsync(string id, LocationSupportUpdateInputVo input);
  29 +}
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/DbModels/FlLocationSupportDbEntity.cs 0 → 100644
  1 +using SqlSugar;
  2 +
  3 +namespace FoodLabeling.Application.Services.DbModels;
  4 +
  5 +/// <summary>
  6 +/// 门店 Support 联系方式(每个门店仅一条,对 App Support 页展示)
  7 +/// </summary>
  8 +[SugarTable("fl_location_support")]
  9 +public class FlLocationSupportDbEntity
  10 +{
  11 + [SugarColumn(IsPrimaryKey = true)]
  12 + public string Id { get; set; } = string.Empty;
  13 +
  14 + public bool IsDeleted { get; set; }
  15 +
  16 + public DateTime CreationTime { get; set; }
  17 +
  18 + public string? CreatorId { get; set; }
  19 +
  20 + public string? LastModifierId { get; set; }
  21 +
  22 + public DateTime? LastModificationTime { get; set; }
  23 +
  24 + public string LocationId { get; set; } = string.Empty;
  25 +
  26 + public string SupportPhone { get; set; } = string.Empty;
  27 +
  28 + public string SupportEmail { get; set; } = string.Empty;
  29 +}
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/FoodLabeling.Application/Services/LocationSupportAppService.cs 0 → 100644
  1 +using FoodLabeling.Application.Contracts.Dtos.LocationSupport;
  2 +using FoodLabeling.Application.Contracts.IServices;
  3 +using FoodLabeling.Application.Services.DbModels;
  4 +using FoodLabeling.Domain.Entities;
  5 +using Volo.Abp;
  6 +using Volo.Abp.Application.Services;
  7 +using Volo.Abp.Guids;
  8 +using Volo.Abp.Uow;
  9 +using Yi.Framework.SqlSugarCore.Abstractions;
  10 +
  11 +namespace FoodLabeling.Application.Services;
  12 +
  13 +/// <summary>
  14 +/// 门店 Support 联系方式(后台设置,App 展示)
  15 +/// </summary>
  16 +public class LocationSupportAppService : ApplicationService, ILocationSupportAppService
  17 +{
  18 + private readonly ISqlSugarDbContext _dbContext;
  19 + private readonly IGuidGenerator _guidGenerator;
  20 +
  21 + public LocationSupportAppService(ISqlSugarDbContext dbContext, IGuidGenerator guidGenerator)
  22 + {
  23 + _dbContext = dbContext;
  24 + _guidGenerator = guidGenerator;
  25 + }
  26 +
  27 + /// <inheritdoc />
  28 + public async Task<LocationSupportGetOutputDto?> GetByLocationIdAsync(string locationId)
  29 + {
  30 + var lid = locationId?.Trim();
  31 + if (string.IsNullOrWhiteSpace(lid))
  32 + {
  33 + throw new UserFriendlyException("门店Id不能为空");
  34 + }
  35 +
  36 + var entity = await _dbContext.SqlSugarClient.Queryable<FlLocationSupportDbEntity>()
  37 + .FirstAsync(x => !x.IsDeleted && x.LocationId == lid);
  38 + if (entity is null)
  39 + {
  40 + return null;
  41 + }
  42 +
  43 + return await MapOutputAsync(entity);
  44 + }
  45 +
  46 + /// <inheritdoc />
  47 + [UnitOfWork]
  48 + public async Task<LocationSupportGetOutputDto> CreateAsync(LocationSupportCreateInputVo input)
  49 + {
  50 + if (input is null)
  51 + {
  52 + throw new UserFriendlyException("入参不能为空");
  53 + }
  54 +
  55 + var locationId = NormalizeLocationId(input.LocationId);
  56 + var phone = NormalizeRequired(input.SupportPhone, "Support 电话不能为空");
  57 + var email = NormalizeRequired(input.SupportEmail, "Support 邮箱不能为空");
  58 + EnsureEmailFormat(email);
  59 +
  60 + await EnsureLocationExistsAsync(locationId);
  61 +
  62 + var existed = await _dbContext.SqlSugarClient.Queryable<FlLocationSupportDbEntity>()
  63 + .AnyAsync(x => !x.IsDeleted && x.LocationId == locationId);
  64 + if (existed)
  65 + {
  66 + throw new UserFriendlyException("该门店已配置 Support 联系方式,请使用编辑接口");
  67 + }
  68 +
  69 + var now = Clock.Now;
  70 + var entity = new FlLocationSupportDbEntity
  71 + {
  72 + Id = _guidGenerator.Create().ToString(),
  73 + IsDeleted = false,
  74 + CreationTime = now,
  75 + CreatorId = CurrentUser?.Id?.ToString(),
  76 + LastModificationTime = now,
  77 + LastModifierId = CurrentUser?.Id?.ToString(),
  78 + LocationId = locationId,
  79 + SupportPhone = phone,
  80 + SupportEmail = email
  81 + };
  82 +
  83 + await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync();
  84 + return (await MapOutputAsync(entity))!;
  85 + }
  86 +
  87 + /// <inheritdoc />
  88 + [UnitOfWork]
  89 + public async Task<LocationSupportGetOutputDto> UpdateAsync(string id, LocationSupportUpdateInputVo input)
  90 + {
  91 + var supportId = id?.Trim();
  92 + if (string.IsNullOrWhiteSpace(supportId))
  93 + {
  94 + throw new UserFriendlyException("联系方式Id不能为空");
  95 + }
  96 +
  97 + if (input is null)
  98 + {
  99 + throw new UserFriendlyException("入参不能为空");
  100 + }
  101 +
  102 + var entity = await _dbContext.SqlSugarClient.Queryable<FlLocationSupportDbEntity>()
  103 + .FirstAsync(x => !x.IsDeleted && x.Id == supportId);
  104 + if (entity is null)
  105 + {
  106 + throw new UserFriendlyException("联系方式记录不存在");
  107 + }
  108 +
  109 + var locationId = NormalizeLocationId(input.LocationId);
  110 + var phone = NormalizeRequired(input.SupportPhone, "Support 电话不能为空");
  111 + var email = NormalizeRequired(input.SupportEmail, "Support 邮箱不能为空");
  112 + EnsureEmailFormat(email);
  113 + await EnsureLocationExistsAsync(locationId);
  114 +
  115 + var conflict = await _dbContext.SqlSugarClient.Queryable<FlLocationSupportDbEntity>()
  116 + .AnyAsync(x => !x.IsDeleted && x.LocationId == locationId && x.Id != supportId);
  117 + if (conflict)
  118 + {
  119 + throw new UserFriendlyException("目标门店已配置 Support 联系方式,一个门店仅允许一条");
  120 + }
  121 +
  122 + entity.LocationId = locationId;
  123 + entity.SupportPhone = phone;
  124 + entity.SupportEmail = email;
  125 + entity.LastModificationTime = Clock.Now;
  126 + entity.LastModifierId = CurrentUser?.Id?.ToString();
  127 +
  128 + await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync();
  129 + return (await MapOutputAsync(entity))!;
  130 + }
  131 +
  132 + private string NormalizeLocationId(string? locationId)
  133 + {
  134 + var lid = locationId?.Trim();
  135 + if (string.IsNullOrWhiteSpace(lid))
  136 + {
  137 + throw new UserFriendlyException("门店Id不能为空");
  138 + }
  139 +
  140 + if (!Guid.TryParse(lid, out _))
  141 + {
  142 + throw new UserFriendlyException("门店Id格式不正确");
  143 + }
  144 +
  145 + return lid;
  146 + }
  147 +
  148 + private static string NormalizeRequired(string? value, string message)
  149 + {
  150 + var normalized = value?.Trim();
  151 + if (string.IsNullOrWhiteSpace(normalized))
  152 + {
  153 + throw new UserFriendlyException(message);
  154 + }
  155 +
  156 + return normalized;
  157 + }
  158 +
  159 + private static void EnsureEmailFormat(string email)
  160 + {
  161 + if (!email.Contains("@", StringComparison.Ordinal) || email.StartsWith("@", StringComparison.Ordinal) ||
  162 + email.EndsWith("@", StringComparison.Ordinal))
  163 + {
  164 + throw new UserFriendlyException("Support 邮箱格式不正确");
  165 + }
  166 + }
  167 +
  168 + private async Task EnsureLocationExistsAsync(string locationId)
  169 + {
  170 + if (!Guid.TryParse(locationId, out var gid))
  171 + {
  172 + throw new UserFriendlyException("门店Id格式不正确");
  173 + }
  174 +
  175 + var exists = await _dbContext.SqlSugarClient.Queryable<LocationAggregateRoot>()
  176 + .AnyAsync(x => !x.IsDeleted && x.Id == gid);
  177 + if (!exists)
  178 + {
  179 + throw new UserFriendlyException("门店不存在");
  180 + }
  181 + }
  182 +
  183 + private async Task<LocationSupportGetOutputDto?> MapOutputAsync(FlLocationSupportDbEntity? entity)
  184 + {
  185 + if (entity is null)
  186 + {
  187 + return null;
  188 + }
  189 +
  190 + string? locationName = null;
  191 + if (Guid.TryParse(entity.LocationId, out var lid))
  192 + {
  193 + var loc = await _dbContext.SqlSugarClient.Queryable<LocationAggregateRoot>()
  194 + .FirstAsync(x => !x.IsDeleted && x.Id == lid);
  195 + locationName = loc?.LocationName?.Trim();
  196 + }
  197 +
  198 + return new LocationSupportGetOutputDto
  199 + {
  200 + Id = entity.Id,
  201 + LocationId = entity.LocationId,
  202 + LocationName = locationName,
  203 + SupportPhone = entity.SupportPhone,
  204 + SupportEmail = entity.SupportEmail
  205 + };
  206 + }
  207 +}
  208 +
... ...
美国版/Food Labeling Management Code/Yi.Abp.Net8/module/food-labeling-us/scripts/fl_location_support_create.sql 0 → 100644
  1 +-- 门店 Support 联系方式(每个门店仅一条)
  2 +CREATE TABLE IF NOT EXISTS `fl_location_support` (
  3 + `Id` varchar(50) NOT NULL COMMENT '主键',
  4 + `IsDeleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除',
  5 + `CreationTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  6 + `CreatorId` varchar(50) DEFAULT NULL COMMENT '创建人',
  7 + `LastModifierId` varchar(50) DEFAULT NULL COMMENT '最后修改人',
  8 + `LastModificationTime` datetime DEFAULT NULL COMMENT '最后修改时间',
  9 + `LocationId` varchar(50) NOT NULL COMMENT '门店Id(对应location.Id)',
  10 + `SupportPhone` varchar(100) NOT NULL COMMENT 'Support 电话',
  11 + `SupportEmail` varchar(200) NOT NULL COMMENT 'Support 邮箱',
  12 + PRIMARY KEY (`Id`),
  13 + UNIQUE KEY `uk_fl_location_support_locationid` (`LocationId`),
  14 + KEY `idx_fl_location_support_isdeleted` (`IsDeleted`)
  15 +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='门店 Support 联系方式';
  16 +
... ...
项目相关文档/美国版App登录接口说明.md
... ... @@ -285,6 +285,96 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
285 285  
286 286 ---
287 287  
  288 +## 接口 6:门店 Support 联系方式(App 展示)
  289 +
  290 +用于 App「Support」页面读取当前门店的联系方式(电话、邮箱)。
  291 +后台由 `LocationSupportAppService` 维护,规则是 **一个门店仅允许一条** 联系方式记录。
  292 +
  293 +### HTTP
  294 +
  295 +- **方法**:`GET`
  296 +- **路径**:`/api/app/location-support/by-location-id?locationId={locationId}`(以 Swagger 中 `LocationSupport` 为准)
  297 +- **鉴权**:需要登录(`Authorization: Bearer {token}`)
  298 +
  299 +### 请求参数
  300 +
  301 +| 参数名 | 位置 | 类型 | 必填 | 说明 |
  302 +|--------|------|------|------|------|
  303 +| `locationId` | Query | string | 是 | 门店主键(Guid 字符串) |
  304 +
  305 +### 响应体(LocationSupportGetOutputDto)
  306 +
  307 +| 字段(JSON) | 类型 | 说明 |
  308 +|--------------|------|------|
  309 +| `id` | string | 联系方式主键 |
  310 +| `locationId` | string | 门店主键 |
  311 +| `locationName` | string \| null | 门店名称 |
  312 +| `supportPhone` | string | Support 电话 |
  313 +| `supportEmail` | string | Support 邮箱 |
  314 +
  315 +> 若门店尚未配置联系方式,接口返回 `null`。
  316 +
  317 +### 响应示例
  318 +
  319 +```json
  320 +{
  321 + "id": "3a2f4fda-1a93-4a35-9b98-95dca7bb5d2a",
  322 + "locationId": "a2696b9e-2277-11f1-b4c6-00163e0c7c4f",
  323 + "locationName": "Downtown Store",
  324 + "supportPhone": "1-800-SUPPORT",
  325 + "supportEmail": "support@medvantage.com"
  326 +}
  327 +```
  328 +
  329 +---
  330 +
  331 +## 后台维护接口:Location Support(新增/编辑)
  332 +
  333 +仅后台管理端使用,用于配置 App Support 页面展示内容。
  334 +
  335 +### 接口 A:新增门店联系方式
  336 +
  337 +- **方法**:`POST`
  338 +- **路径**:`/api/app/location-support`
  339 +- **Content-Type**:`application/json`
  340 +
  341 +请求体(LocationSupportCreateInputVo):
  342 +
  343 +```json
  344 +{
  345 + "locationId": "a2696b9e-2277-11f1-b4c6-00163e0c7c4f",
  346 + "supportPhone": "1-800-SUPPORT",
  347 + "supportEmail": "support@medvantage.com"
  348 +}
  349 +```
  350 +
  351 +约束:
  352 +- 同一 `locationId` 只能新增一条,重复会报错:`该门店已配置 Support 联系方式,请使用编辑接口`
  353 +
  354 +### 接口 B:编辑门店联系方式
  355 +
  356 +- **方法**:`PUT`
  357 +- **路径**:`/api/app/location-support/{id}`
  358 +- **Content-Type**:`application/json`
  359 +
  360 +请求体(LocationSupportUpdateInputVo)与新增一致:
  361 +
  362 +```json
  363 +{
  364 + "locationId": "a2696b9e-2277-11f1-b4c6-00163e0c7c4f",
  365 + "supportPhone": "1-800-SUPPORT",
  366 + "supportEmail": "support@medvantage.com"
  367 +}
  368 +```
  369 +
  370 +常见错误:
  371 +- `门店Id不能为空` / `门店Id格式不正确`
  372 +- `Support 电话不能为空`
  373 +- `Support 邮箱不能为空` / `Support 邮箱格式不正确`
  374 +- `目标门店已配置 Support 联系方式,一个门店仅允许一条`
  375 +
  376 +---
  377 +
288 378 ## 与其他登录方式的区别
289 379  
290 380 | 场景 | 说明 |
... ...