refactor: 拆分微信API到独立项目并迁移相关代码

1.  新增WeChatApi独立项目,将原WebApi中的微信相关控制器迁移至新项目
2.  新增后台权限管理相关实体、服务接口和基础控制器
3.  完善管理员用户服务,增加角色关联查询和赋值逻辑
4.  修复WebApi Swagger文档过滤微信API版本的问题
5.  更新解决方案文件,添加新的微信API项目引用
6.  新增微信API基础配置文件和项目属性配置
This commit is contained in:
glz
2026-07-06 14:21:21 +08:00
parent f8874ff60c
commit a04b4be7e5
32 changed files with 1669 additions and 158 deletions

View File

@ -0,0 +1,52 @@
using Microsoft.AspNetCore.Mvc;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Dto.Bag;
namespace QYZH.InteractiveMagazine.WeChatApi.Controllers;
/// <summary>
/// 小程序背包控制器
/// </summary>
public class BagController(IWxMallService mallService, ILogger<BagController> logger) : WeChatBaseController
{
/// <summary>
/// 获取背包物品列表
/// </summary>
/// <param name="itemType">物品类型筛选(可选): MakeUpCard, PetBg</param>
[HttpGet("items")]
public async Task<BaseResponse<List<UserBagOutput>>> GetBagItems([FromQuery] string? itemType = null)
{
var userId = GetCurrentUserId();
if (userId == 0) return Fail("未获取到用户信息") as dynamic;
var items = await mallService.GetBagItemsAsync(userId, itemType);
return Success(items);
}
/// <summary>
/// 使用背包物品(补签卡等消耗品)
/// </summary>
[HttpPost("useItem")]
public async Task<BaseResponse<UseItemOutput>> UseItem([FromBody] UseItemInput input)
{
var userId = GetCurrentUserId();
if (userId == 0) return Fail("未获取到用户信息") as dynamic;
var result = await mallService.UseItemAsync(userId, input);
return Success(result);
}
/// <summary>
/// 宠物换肤
/// </summary>
[HttpPost("equipSkin")]
public async Task<BaseResponse<object>> EquipSkin([FromBody] EquipSkinInput input)
{
var userId = GetCurrentUserId();
if (userId == 0) return Fail("未获取到用户信息") as dynamic;
await mallService.EquipSkinAsync(userId, input);
return Success<object>(null!, input.SkinId == 0 ? "已恢复默认皮肤" : "换肤成功");
}
}

View File

@ -0,0 +1,144 @@
using Microsoft.AspNetCore.Mvc;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Dto.CheckIn;
namespace QYZH.InteractiveMagazine.WeChatApi.Controllers;
/// <summary>
/// 签到控制器
/// </summary>
public class CheckInController : WeChatBaseController
{
private readonly ICheckInService _checkInService;
private readonly ILogger<CheckInController> _logger;
public CheckInController(ICheckInService checkInService, ILogger<CheckInController> logger)
{
_checkInService = checkInService;
_logger = logger;
}
/// <summary>
/// 用户签到
/// </summary>
/// <returns>签到结果(含奖励详情和余额)</returns>
[HttpPost("checkIn")]
public async Task<BaseResponse<CheckInOutput>> CheckInAsync()
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<CheckInOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await _checkInService.CheckInAsync(userId);
return Success(result, "签到成功");
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "签到业务异常: {Message}", ex.Message);
return BaseResponse<CheckInOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "签到系统异常");
return BaseResponse<CheckInOutput>.Fail("签到失败,请稍后重试");
}
}
/// <summary>
/// 获取签到信息(今日状态、连续天数、最近记录)
/// </summary>
/// <returns>签到信息</returns>
[HttpGet("info")]
public async Task<BaseResponse<CheckInInfoOutput>> GetCheckInInfoAsync()
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<CheckInInfoOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await _checkInService.GetCheckInInfoAsync(userId);
return Success(result);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "获取签到信息业务异常: {Message}", ex.Message);
return BaseResponse<CheckInInfoOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "获取签到信息系统异常");
return BaseResponse<CheckInInfoOutput>.Fail("获取签到信息失败,请稍后重试");
}
}
/// <summary>
/// 补签(消耗补签卡,补签历史漏签日期)
/// </summary>
/// <param name="input">补签输入(目标日期)</param>
/// <returns>补签结果(含奖励详情和余额)</returns>
[HttpPost("makeUp")]
public async Task<BaseResponse<CheckInOutput>> MakeUpCheckInAsync([FromBody] MakeUpCheckInInput input)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<CheckInOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await _checkInService.MakeUpCheckInAsync(userId, input.TargetDate);
return Success(result, "补签成功");
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "补签业务异常: {Message}", ex.Message);
return BaseResponse<CheckInOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "补签系统异常");
return BaseResponse<CheckInOutput>.Fail("补签失败,请稍后重试");
}
}
/// <summary>
/// 获取可补签的日期列表(历史漏签日期)
/// </summary>
/// <param name="days">往前查看天数默认30天</param>
/// <returns>漏签日期列表</returns>
[HttpGet("missedDates")]
public async Task<BaseResponse<List<DateTime>>> GetMissedDatesAsync([FromQuery] int days = 30)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<List<DateTime>>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await _checkInService.GetMissedDatesAsync(userId, days);
return Success(result);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "获取可补签日期业务异常: {Message}", ex.Message);
return BaseResponse<List<DateTime>>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "获取可补签日期系统异常");
return BaseResponse<List<DateTime>>.Fail("获取可补签日期失败,请稍后重试");
}
}
}

View File

@ -0,0 +1,129 @@
using Microsoft.AspNetCore.Mvc;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
namespace QYZH.InteractiveMagazine.WeChatApi.Controllers;
/// <summary>
/// 小程序社区控制器
/// </summary>
public class CommunityController(IWeChatCommunityService communityService, ILogger<CommunityController> logger) : WeChatBaseController
{
/// <summary>
/// 获取社区Feed流翻页
/// </summary>
/// <param name="cursor">游标上一页最后一条消息ID首次不传</param>
[HttpGet("feed")]
public async Task<BaseResponse<WxFeedOutput>> GetFeed([FromQuery] long? cursor = null)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<WxFeedOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await communityService.GetFeedAsync(userId, cursor);
return Success(result);
}
catch (BusinessException ex)
{
logger.LogWarning(ex, "获取社区Feed业务异常: {Message}", ex.Message);
return BaseResponse<WxFeedOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
logger.LogError(ex, "获取社区Feed系统异常");
return BaseResponse<WxFeedOutput>.Fail("获取社区内容失败,请稍后重试");
}
}
/// <summary>
/// 下拉刷新社区Feed
/// </summary>
[HttpGet("refresh")]
public async Task<BaseResponse<WxFeedOutput>> RefreshFeed()
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<WxFeedOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await communityService.RefreshFeedAsync(userId);
return Success(result);
}
catch (BusinessException ex)
{
logger.LogWarning(ex, "刷新社区Feed业务异常: {Message}", ex.Message);
return BaseResponse<WxFeedOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
logger.LogError(ex, "刷新社区Feed系统异常");
return BaseResponse<WxFeedOutput>.Fail("刷新社区内容失败,请稍后重试");
}
}
/// <summary>
/// 点赞
/// </summary>
[HttpPost("like")]
public async Task<BaseResponse<WxLikeOutput>> Like([FromBody] WxLikeInput input)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<WxLikeOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await communityService.LikeAsync(userId, input);
return Success(result, "点赞成功");
}
catch (BusinessException ex)
{
logger.LogWarning(ex, "点赞业务异常: {Message}", ex.Message);
return BaseResponse<WxLikeOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
logger.LogError(ex, "点赞系统异常,参数:{Input}", input);
return BaseResponse<WxLikeOutput>.Fail("点赞失败,请稍后重试");
}
}
/// <summary>
/// 取消点赞
/// </summary>
[HttpPost("unlike")]
public async Task<BaseResponse<WxLikeOutput>> Unlike([FromBody] WxLikeInput input)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<WxLikeOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await communityService.UnlikeAsync(userId, input.MessageId);
return Success(result, "已取消点赞");
}
catch (BusinessException ex)
{
logger.LogWarning(ex, "取消点赞业务异常: {Message}", ex.Message);
return BaseResponse<WxLikeOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
logger.LogError(ex, "取消点赞系统异常,参数:{Input}", input);
return BaseResponse<WxLikeOutput>.Fail("取消点赞失败,请稍后重试");
}
}
}

View File

@ -0,0 +1,90 @@
using Microsoft.AspNetCore.Mvc;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
namespace QYZH.InteractiveMagazine.WeChatApi.Controllers;
/// <summary>
/// 小程序期刊管理控制器
/// </summary>
public class JournalController : WeChatBaseController
{
private readonly IUserJournalService _userJournalService;
private readonly ILogger<JournalController> _logger;
/// <summary>
/// 初始化小程序期刊控制器
/// </summary>
/// <param name="userJournalService">用户期刊关联服务</param>
/// <param name="logger">日志服务</param>
public JournalController(
IUserJournalService userJournalService,
ILogger<JournalController> logger)
{
_userJournalService = userJournalService;
_logger = logger;
}
/// <summary>
/// 用户扫码绑定期刊
/// </summary>
/// <param name="input">绑定输入JournalId、JournalInstanceId 从扫码内容解析Type 默认 Subscribe</param>
/// <returns>绑定结果</returns>
[HttpPost("bind")]
public async Task<BaseResponse<BindJournalOutput>> BindAsync([FromBody] BindJournalInput input)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<BindJournalOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await _userJournalService.BindJournalAsync(userId, input);
return Success(result, "绑定期刊成功");
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "绑定期刊业务异常: {Message}", ex.Message);
return BaseResponse<BindJournalOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "绑定期刊系统异常,参数:{Input}", input);
return BaseResponse<BindJournalOutput>.Fail("绑定期刊失败,请稍后重试");
}
}
/// <summary>
/// 获取当前用户的期刊绑定列表
/// </summary>
/// <param name="input">查询条件期刊Id、实例Id、关联类型</param>
/// <returns>分页结果</returns>
[HttpPost("list")]
public async Task<BaseResponse<PageListModel<BindJournalOutput>>> GetListAsync([FromBody] UserJournalQueryInput input)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<PageListModel<BindJournalOutput>>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await _userJournalService.GetUserJournalsAsync(userId, input);
return Success(result);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "查询期刊绑定列表业务异常: {Message}", ex.Message);
return BaseResponse<PageListModel<BindJournalOutput>>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "查询期刊绑定列表系统异常");
return BaseResponse<PageListModel<BindJournalOutput>>.Fail("查询期刊绑定列表失败,请稍后重试");
}
}
}

View File

@ -0,0 +1,70 @@
using Microsoft.AspNetCore.Mvc;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Dto.Mall;
namespace QYZH.InteractiveMagazine.WeChatApi.Controllers;
/// <summary>
/// 小程序商城控制器
/// </summary>
public class MallController(IWxMallService mallService, ILogger<MallController> logger) : WeChatBaseController
{
/// <summary>
/// 获取商城商品列表
/// </summary>
/// <param name="type">商品类型筛选(可选): MakeUpCard, PetBg</param>
[HttpGet("products")]
public async Task<BaseResponse<List<WxProductOutput>>> GetProducts([FromQuery] string? type = null)
{
var userId = GetCurrentUserId();
if (userId == 0) return Fail("未获取到用户信息") as dynamic;
var products = await mallService.GetProductsAsync(userId, type);
return Success(products);
}
/// <summary>
/// 获取商品详情
/// </summary>
/// <param name="id">商品Id</param>
[HttpGet("product/{id}")]
public async Task<BaseResponse<WxProductOutput>> GetProductDetail(long id)
{
var userId = GetCurrentUserId();
if (userId == 0) return Fail("未获取到用户信息") as dynamic;
var product = await mallService.GetProductDetailAsync(userId, id);
if (product == null)
return BaseResponse<WxProductOutput>.Fail(ResultCode.DENY, "商品不存在或已下架");
return Success(product);
}
/// <summary>
/// 积分兑换商品
/// </summary>
[HttpPost("exchange")]
public async Task<BaseResponse<ExchangeOutput>> Exchange([FromBody] ExchangeInput input)
{
var userId = GetCurrentUserId();
if (userId == 0) return Fail("未获取到用户信息") as dynamic;
var result = await mallService.ExchangeAsync(userId, input);
return Success(result);
}
/// <summary>
/// 获取我的兑换记录
/// </summary>
/// <param name="limit">返回数量默认20</param>
[HttpGet("exchangeRecords")]
public async Task<BaseResponse<List<ExchangeRecordOutput>>> GetExchangeRecords([FromQuery] int limit = 20)
{
var userId = GetCurrentUserId();
if (userId == 0) return Fail("未获取到用户信息") as dynamic;
var records = await mallService.GetExchangeRecordsAsync(userId, limit);
return Success(records);
}
}

View File

@ -0,0 +1,99 @@
using Microsoft.AspNetCore.Mvc;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
namespace QYZH.InteractiveMagazine.WeChatApi.Controllers;
/// <summary>
/// 小程序勋章控制器
/// </summary>
public class MedalController(IMedalService medalService, ILogger<MedalController> logger) : WeChatBaseController
{
/// <summary>
/// 获取所有勋章列表(含当前用户拥有状态)
/// </summary>
[HttpGet("all")]
public async Task<BaseResponse<List<WxMedalListOutput>>> GetAllMedals()
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<List<WxMedalListOutput>>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await medalService.GetAllMedalsAsync(userId);
return Success(result);
}
catch (BusinessException ex)
{
logger.LogWarning(ex, "获取勋章列表业务异常: {Message}", ex.Message);
return BaseResponse<List<WxMedalListOutput>>.Fail(ex.Message);
}
catch (Exception ex)
{
logger.LogError(ex, "获取勋章列表系统异常");
return BaseResponse<List<WxMedalListOutput>>.Fail("获取勋章列表失败,请稍后重试");
}
}
/// <summary>
/// 获取用户已拥有的勋章列表
/// </summary>
[HttpGet("my")]
public async Task<BaseResponse<List<WxUserMedalOutput>>> GetUserMedals()
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<List<WxUserMedalOutput>>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await medalService.GetUserMedalsAsync(userId);
return Success(result);
}
catch (BusinessException ex)
{
logger.LogWarning(ex, "获取用户勋章列表业务异常: {Message}", ex.Message);
return BaseResponse<List<WxUserMedalOutput>>.Fail(ex.Message);
}
catch (Exception ex)
{
logger.LogError(ex, "获取用户勋章列表系统异常");
return BaseResponse<List<WxUserMedalOutput>>.Fail("获取用户勋章列表失败,请稍后重试");
}
}
/// <summary>
/// 激活/获得勋章
/// </summary>
[HttpPost("activate")]
public async Task<BaseResponse<object>> ActivateMedal([FromBody] WxMedalActivateInput input)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<object>.Fail(ResultCode.DENY, "未获取到用户信息");
}
await medalService.ActivateMedalAsync(userId, input);
return Success<object>(null!, "勋章激活成功");
}
catch (BusinessException ex)
{
logger.LogWarning(ex, "激活勋章业务异常: {Message}", ex.Message);
return BaseResponse<object>.Fail(ex.Message);
}
catch (Exception ex)
{
logger.LogError(ex, "激活勋章系统异常,参数:{Input}", input);
return BaseResponse<object>.Fail("激活勋章失败,请稍后重试");
}
}
}

View File

@ -0,0 +1,114 @@
using Microsoft.AspNetCore.Mvc;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Dto.Pet;
namespace QYZH.InteractiveMagazine.WeChatApi.Controllers;
/// <summary>
/// 小程序宠物管理控制器
/// </summary>
public class PetController : WeChatBaseController
{
private readonly IPetService _petService;
private readonly ILogger<PetController> _logger;
public PetController(IPetService petService, ILogger<PetController> logger)
{
_petService = petService;
_logger = logger;
}
/// <summary>
/// 获取当前用户的宠物信息
/// </summary>
[HttpGet("mine")]
public async Task<BaseResponse<PetOutput>> GetMyPetAsync()
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<PetOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var pet = await _petService.GetPetByUserIdAsync(userId);
if (pet == null)
{
return BaseResponse<PetOutput>.Fail("未找到宠物信息");
}
return Success(pet);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "获取宠物信息业务异常: {Message}", ex.Message);
return BaseResponse<PetOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "获取宠物信息系统异常");
return BaseResponse<PetOutput>.Fail("获取宠物信息失败,请稍后重试");
}
}
/// <summary>
/// 喂养宠物(增加成长值,触发进化检查)
/// </summary>
[HttpPost("feed")]
public async Task<BaseResponse<FeedPetOutput>> FeedPetAsync([FromBody] FeedPetInput input)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<FeedPetOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await _petService.FeedPetAsync(userId, input);
return Success(result, "喂养成功");
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "喂养宠物业务异常: {Message}", ex.Message);
return BaseResponse<FeedPetOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "喂养宠物系统异常,参数:{@Input}", input);
return BaseResponse<FeedPetOutput>.Fail("喂养宠物失败,请稍后重试");
}
}
/// <summary>
/// 获取宠物喂养记录列表
/// </summary>
[HttpGet("records/{petId}")]
public async Task<BaseResponse<PageListModel<FeedingRecordOutput>>> GetFeedingRecordsAsync(long petId, [FromQuery] PageQueryModel pageQuery)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<PageListModel<FeedingRecordOutput>>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await _petService.GetFeedingRecordsAsync(userId, petId, pageQuery);
return Success(result);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "查询喂养记录业务异常: {Message}", ex.Message);
return BaseResponse<PageListModel<FeedingRecordOutput>>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "查询喂养记录系统异常");
return BaseResponse<PageListModel<FeedingRecordOutput>>.Fail("查询喂养记录失败,请稍后重试");
}
}
}

View File

@ -0,0 +1,268 @@
using Microsoft.AspNetCore.Mvc;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Dto.UserAnswerTaskService;
using System.ComponentModel.DataAnnotations;
namespace QYZH.InteractiveMagazine.WeChatApi.Controllers;
/// <summary>
/// 小程序首页控制器
/// </summary>
public class UserAnswerTaskController : WeChatBaseController
{
private readonly IUserAnswerTaskService _userAnswerTaskService;
private readonly ILogger<UserAnswerTaskController> _logger;
/// <summary>
/// 构造函数
/// </summary>
public UserAnswerTaskController(
IUserAnswerTaskService userAnswerTaskService,
ILogger<UserAnswerTaskController> logger)
{
_userAnswerTaskService = userAnswerTaskService;
_logger = logger;
}
private const long ConstUserId = 821752253853766;
/// <summary>
/// 获取当前用户的期刊学习进度列表
/// </summary>
/// <returns>按期刊分组的学习进度数据</returns>
[HttpGet("progress")]
public async Task<BaseResponse<List<JournalProgressOutput>>> GetProgressAsync()
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<List<JournalProgressOutput>>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await _userAnswerTaskService.GetJournalProgressAsync(userId);
return Success(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "获取学习进度失败UserId: {UserId}", GetCurrentUserId());
return BaseResponse<List<JournalProgressOutput>>.Fail("获取学习进度失败,请稍后重试");
}
}
/// <summary>
/// 获取目录列表(按一级目录分组,二级目录含完成状态)
/// </summary>
/// <param name="journalId">期刊Id</param>
/// <returns>按一级目录分组的目录列表</returns>
[HttpGet("catalog-list/{journalId:long}")]
public async Task<BaseResponse<List<CatalogListOutput>>> GetCatalogListAsync([Required(ErrorMessage = "参数错误")] long journalId)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<List<CatalogListOutput>>.Fail(ResultCode.DENY, "未获取到用户信息");
}
if (journalId <= 0)
{
return BaseResponse<List<CatalogListOutput>>.Fail(ResultCode.PARAM_ERROR, "期刊Id不能为空");
}
var result = await _userAnswerTaskService.GetCatalogListAsync(journalId, userId);
return Success(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "获取目录列表失败JournalId: {JournalId}, UserId: {UserId}", journalId, GetCurrentUserId());
return BaseResponse<List<CatalogListOutput>>.Fail("获取目录列表失败,请稍后重试");
}
}
/// <summary>
/// 获取一级目录概览(含二级目录详情、页码范围、任务完成状态)
/// </summary>
/// <param name="catalogId">一级目录Id</param>
/// <returns>一级目录概览数据</returns>
[HttpGet("catalog-overview/{catalogId:long}")]
public async Task<BaseResponse<CatalogOverviewOutput>> GetCatalogOverviewAsync([Required(ErrorMessage = "参数错误")] long catalogId)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<CatalogOverviewOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
}
if (catalogId <= 0)
{
return BaseResponse<CatalogOverviewOutput>.Fail(ResultCode.PARAM_ERROR, "目录Id不能为空");
}
var result = await _userAnswerTaskService.GetCatalogOverviewAsync(catalogId, userId);
if (result == null)
{
return BaseResponse<CatalogOverviewOutput>.Fail("未找到该目录");
}
return Success(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "获取目录概览失败CatalogId: {CatalogId}, UserId: {UserId}", catalogId, GetCurrentUserId());
return BaseResponse<CatalogOverviewOutput>.Fail("获取目录概览失败,请稍后重试");
}
}
/// <summary>
/// 获取用户答题任务列表
/// </summary>
/// <param name="catalogId">二级目录Id</param>
/// <returns>答题任务列表</returns>
[HttpGet("task-list/{catalogId:long}")]
public async Task<BaseResponse<UserAnswerTaskListOutput>> GetTaskListAsync([Required(ErrorMessage = "参数错误")] long catalogId)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<UserAnswerTaskListOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
}
if (catalogId <= 0)
{
return BaseResponse<UserAnswerTaskListOutput>.Fail(ResultCode.PARAM_ERROR, "目录Id不能为空");
}
var result = await _userAnswerTaskService.GetUserAnswerTaskListAsync(catalogId, userId);
if (result == null)
{
return BaseResponse<UserAnswerTaskListOutput>.Fail("未找到该目录");
}
return Success(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "获取任务列表失败CatalogId: {CatalogId}, UserId: {UserId}", catalogId, GetCurrentUserId());
return BaseResponse<UserAnswerTaskListOutput>.Fail("获取任务列表失败,请稍后重试");
}
}
/// <summary>
/// 获取跨页题目详情
/// </summary>
/// <param name="groupId">任务分组ID</param>
/// <returns>跨页题目详情</returns>
[HttpGet("cross-page-task/{groupId:long}")]
public async Task<BaseResponse<CrossPageTaskOutput>> GetCrossPageTaskAsync([Required(ErrorMessage ="参数错误")] long groupId)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<CrossPageTaskOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
}
if (groupId <= 0)
{
return BaseResponse<CrossPageTaskOutput>.Fail(ResultCode.PARAM_ERROR, "任务Id不能为空");
}
var result = await _userAnswerTaskService.GetCrossPageTaskAsync(groupId, userId);
if (result == null)
{
return BaseResponse<CrossPageTaskOutput>.Fail("未找到该任务");
}
return Success(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "获取跨页题目失败JournalPageTaskGroupId: {groupId}, UserId: {UserId}", groupId, GetCurrentUserId());
return BaseResponse<CrossPageTaskOutput>.Fail("获取跨页题目失败,请稍后重试");
}
}
/// <summary>
/// 领取任务积分
/// </summary>
/// <param name="groupId">任务分组IdGroupId</param>
/// <returns>领取结果</returns>
[HttpPost("claim-points/{groupId:long}")]
public async Task<BaseResponse<ClaimTaskPointsOutput>> ClaimTaskPointsAsync([Required(ErrorMessage = "参数错误")] long groupId)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<ClaimTaskPointsOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
}
if (groupId <= 0)
{
return BaseResponse<ClaimTaskPointsOutput>.Fail(ResultCode.PARAM_ERROR, "任务分组Id不能为空");
}
var result = await _userAnswerTaskService.ClaimTaskPointsAsync(groupId, userId);
return Success(result);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "领取任务积分业务异常groupId: {groupId}, UserId: {UserId}", groupId, GetCurrentUserId());
return BaseResponse<ClaimTaskPointsOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "领取任务积分失败groupId: {groupId}, UserId: {UserId}", groupId, GetCurrentUserId());
return BaseResponse<ClaimTaskPointsOutput>.Fail("领取任务积分失败,请稍后重试");
}
}
/// <summary>
/// 批量领取任务积分(一键领取)
/// </summary>
/// <param name="input">批量领取参数</param>
/// <returns>批量领取结果</returns>
[HttpPost("batch-claim-points")]
public async Task<BaseResponse<BatchClaimPointsOutput>> BatchClaimPointsAsync([FromBody] BatchClaimPointsInput input)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<BatchClaimPointsOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
}
if (input == null || input.GroupIds == null || input.GroupIds.Count == 0)
{
return BaseResponse<BatchClaimPointsOutput>.Fail(ResultCode.PARAM_ERROR, "任务分组Id列表不能为空");
}
var result = await _userAnswerTaskService.BatchClaimPointsAsync(input, userId);
return Success(result);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "批量领取积分业务异常UserId: {UserId}", GetCurrentUserId());
return BaseResponse<BatchClaimPointsOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "批量领取积分失败UserId: {UserId}", GetCurrentUserId());
return BaseResponse<BatchClaimPointsOutput>.Fail("批量领取积分失败,请稍后重试");
}
}
}

View File

@ -0,0 +1,185 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.WeChat;
namespace QYZH.InteractiveMagazine.WeChatApi.Controllers;
/// <summary>
/// 微信小程序认证控制器
/// </summary>
public class WeChatAuthController : WeChatBaseController
{
private readonly IWeChatAuthService _weChatAuthService;
private readonly ILogger<WeChatAuthController> _logger;
public WeChatAuthController(IWeChatAuthService weChatAuthService, ILogger<WeChatAuthController> logger)
{
_weChatAuthService = weChatAuthService;
_logger = logger;
}
/// <summary>
/// 微信小程序登录(首次仅创建 WxUser不自动创建 User
/// </summary>
/// <param name="input">登录输入(含微信 code 和可选的手机号 code</param>
/// <returns>登录结果(含 Token 和用户列表)</returns>
[AllowAnonymous]
[HttpPost("login")]
public async Task<BaseResponse<WeChatLoginOutput>> LoginAsync([FromBody] WeChatLoginInput input)
{
try
{
var result = await _weChatAuthService.LoginAsync(input);
return Success(result);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "微信登录业务异常: {Message}", ex.Message);
return BaseResponse<WeChatLoginOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "微信登录系统异常");
return BaseResponse<WeChatLoginOutput>.Fail("微信登录失败,请稍后重试");
}
}
/// <summary>
/// 微信小程序快捷登录(通过 OpenId 直接登录,用户需已存在)
/// </summary>
/// <param name="input">快捷登录输入(含 OpenId</param>
/// <returns>登录结果(含 Token 和用户列表)</returns>
[AllowAnonymous]
[HttpPost("quickLogin")]
public async Task<BaseResponse<WeChatLoginOutput>> QuickLoginAsync([FromBody] WeChatQuickLoginInput input)
{
try
{
var result = await _weChatAuthService.QuickLoginAsync(input);
return Success(result);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "微信快捷登录业务异常: {Message}", ex.Message);
return BaseResponse<WeChatLoginOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "微信快捷登录系统异常");
return BaseResponse<WeChatLoginOutput>.Fail("快捷登录失败,请稍后重试");
}
}
/// <summary>
/// 切换用户(同一 WxUser 下切换 User 身份)
/// </summary>
[HttpPost("switchUser")]
public async Task<BaseResponse<WeChatSwitchUserOutput>> SwitchUserAsync([FromBody] WeChatSwitchUserInput input)
{
try
{
var wxUserId = GetCurrentWxUserId();
if (wxUserId == null)
return BaseResponse<WeChatSwitchUserOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
var currentUserId = GetCurrentUserId();
var result = await _weChatAuthService.SwitchUserAsync(wxUserId.Value, currentUserId, input);
return Success(result);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "切换用户业务异常: {Message}", ex.Message);
return BaseResponse<WeChatSwitchUserOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "切换用户系统异常");
return BaseResponse<WeChatSwitchUserOutput>.Fail("切换用户失败,请稍后重试");
}
}
/// <summary>
/// 获取当前微信用户下的所有用户列表
/// </summary>
[HttpGet("users")]
public async Task<BaseResponse<List<WxUserOutput>>> GetUsersAsync()
{
try
{
var wxUserId = GetCurrentWxUserId();
if (wxUserId == null)
return BaseResponse<List<WxUserOutput>>.Fail(ResultCode.DENY, "未获取到用户信息");
var result = await _weChatAuthService.GetUsersAsync(wxUserId.Value);
return Success(result);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "获取用户列表业务异常: {Message}", ex.Message);
return BaseResponse<List<WxUserOutput>>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "获取用户列表系统异常");
return BaseResponse<List<WxUserOutput>>.Fail("获取用户列表失败,请稍后重试");
}
}
/// <summary>
/// 在当前微信用户下新增用户(子用户/角色)
/// </summary>
[HttpPost("createUser")]
public async Task<BaseResponse<WxUserOutput>> CreateUserAsync([FromBody] CreateChildUserInput input)
{
try
{
var wxUserId = GetCurrentWxUserId();
if (wxUserId == null)
return BaseResponse<WxUserOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
var result = await _weChatAuthService.CreateUserAsync(wxUserId.Value, input);
return Success(result);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "新增用户业务异常: {Message}", ex.Message);
return BaseResponse<WxUserOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "新增用户系统异常");
return BaseResponse<WxUserOutput>.Fail("新增用户失败,请稍后重试");
}
}
/// <summary>
/// 修改家长名字WxUser.Name
/// </summary>
[HttpPost("updateName")]
public async Task<BaseResponse<WxUserInfoOutput>> UpdateWxUserNameAsync([FromBody] UpdateWxUserNameInput input)
{
try
{
var wxUserId = GetCurrentWxUserId();
if (wxUserId == null)
return BaseResponse<WxUserInfoOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
var result = await _weChatAuthService.UpdateWxUserNameAsync(wxUserId.Value, input);
return Success(result, "修改成功");
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "修改家长名字业务异常: {Message}", ex.Message);
return BaseResponse<WxUserInfoOutput>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "修改家长名字系统异常");
return BaseResponse<WxUserInfoOutput>.Fail("修改家长名字失败,请稍后重试");
}
}
}

View File

@ -0,0 +1,78 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using QYZH.InteractiveMagazine.Infrastructure.Auth;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Enum;
using System.Security.Claims;
namespace QYZH.InteractiveMagazine.WeChatApi.Controllers;
/// <summary>
/// 小程序基础控制器
/// </summary>
[Authorize]
[ApiController]
[Route("wechat/api/[controller]")]
[ApiExplorerSettings(GroupName = nameof(ApiVersionEnum.Wechat))]
public abstract class WeChatBaseController : ControllerBase
{
/// <summary>
/// 获取当前激活用户IDUsers.Id来自 JWT NameIdentifier
/// </summary>
/// <returns>User ID无激活用户时为 0</returns>
protected long GetCurrentUserId()
{
var userIdClaim = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
if (userIdClaim != null && long.TryParse(userIdClaim.Value, out var userId))
{
return userId;
}
return 0;
}
/// <summary>
/// 获取当前微信用户IDWxUser.Id来自 JWT WxUserId claim
/// </summary>
/// <returns>WxUser ID</returns>
protected long? GetCurrentWxUserId()
{
var wxUserIdClaim = User.Claims.FirstOrDefault(c => c.Type == JwtHelper.WxUserIdClaimType);
if (wxUserIdClaim != null && long.TryParse(wxUserIdClaim.Value, out var wxUserId))
{
return wxUserId;
}
return null;
}
/// <summary>
/// 获取当前用户名
/// </summary>
/// <returns>用户名</returns>
protected string? GetCurrentUserName()
{
return User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
}
/// <summary>
/// 成功响应
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
/// <param name="data">数据</param>
/// <param name="message">提示信息</param>
/// <returns>统一响应对象</returns>
protected BaseResponse<T> Success<T>(T data, string message = "操作成功")
{
return BaseResponse<T>.Success(data, message);
}
/// <summary>
/// 失败响应
/// </summary>
/// <param name="message">提示信息</param>
/// <param name="code">状态码</param>
/// <returns>统一响应对象</returns>
protected BaseResponse<object> Fail(string message, int code = 500)
{
return BaseResponse<object>.Fail(ResultCode.GLOBAL_ERROR, message);
}
}

View File

@ -0,0 +1,189 @@
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.OpenApi.Models;
using QYZH.InteractiveMagazine.Common.Extensions;
using QYZH.InteractiveMagazine.Common.Helpers;
using QYZH.InteractiveMagazine.Infrastructure.Autofacs;
using QYZH.InteractiveMagazine.Infrastructure.Context;
using QYZH.InteractiveMagazine.Infrastructure.Extensions;
using QYZH.InteractiveMagazine.Infrastructure.Middleware;
using QYZH.InteractiveMagazine.Infrastructure.RabbitMQ;
using QYZH.InteractiveMagazine.Infrastructure.Redis;
using QYZH.InteractiveMagazine.Infrastructure.SDK;
using QYZH.InteractiveMagazine.Models.Enum;
using QYZH.InteractiveMagazine.Models.Settings;
using QYZH.InteractiveMagazine.Repository;
using QYZH.InteractiveMagazine.Repository.Core;
using Serilog;
using SqlSugar.IOC;
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.SwaggerUI;
using System.Text.Json.Serialization;
using Yitter.IdGenerator;
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("medal-rule-config.json", optional: false, reloadOnChange: true);
YitIdHelper.SetIdGenerator(new IdGeneratorOptions { WorkerId = 1 });
builder.UseAutofac();
builder.InitSqlSugarDb(new IocConfig
{
ConfigId = 0,
DbType = IocDbType.MySql,
ConnectionString = builder.Configuration.GetConnectionString("DefaultConnection"),
IsAutoCloseConnection = true,
});
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(Path.Combine(Directory.GetCurrentDirectory(), "DataProtection")));
builder.Services.AddCSRedisCacheExtension(builder.Configuration.GetSection("RedisSettings"));
builder.Services.AddRabbitMQ(builder.Configuration);
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext()
.CreateLogger();
builder.Host.UseSerilog();
builder.Services.AddControllers(options =>
{
options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true;
options.Filters.Add<ModelValidActionFilterAttribute>();
})
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
options.JsonSerializerOptions.Converters.Add(new JsonConverterUtil.DateTimeConverter());
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
})
.ConfigureApiBehaviorOptions(opt => opt.SuppressModelStateInvalidFilter = true);
builder.Services.AddEndpointsApiExplorer();
builder.AddCorsRegister();
builder.Services.AddHttpClient();
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped(typeof(BaseRepository<>));
builder.Services.AddInfrastructureServices(builder.Configuration, builder.Environment);
builder.Services.AddSDKService(builder.Configuration);
builder.Services.Configure<MedalRuleConfigSettings>(builder.Configuration.GetSection("MedalRuleConfig"));
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
{
options.Level = System.IO.Compression.CompressionLevel.Optimal;
});
builder.Services.Configure<GzipCompressionProviderOptions>(options =>
{
options.Level = System.IO.Compression.CompressionLevel.Fastest;
});
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<BrotliCompressionProvider>();
options.Providers.Add<GzipCompressionProvider>();
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(["image/svg+xml", "application/json", "text/plain"]);
});
builder.Services.AddSwaggerGen(option =>
{
var xmlFile = $"{AppDomain.CurrentDomain.FriendlyName}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
var modelXml = Path.Combine(AppContext.BaseDirectory, "QYZH.InteractiveMagazine.Models.xml");
var version = ApiVersionEnum.Wechat;
option.SwaggerDoc(version.ToString(), new OpenApiInfo
{
Title = AppDomain.CurrentDomain.FriendlyName,
Version = "互动期刊接口文档",
Description = $"{version.GetDescription()}接口Last Modify Time{new FileInfo(xmlPath).LastWriteTime:yyyy-MM-dd HH:mm:ss}"
});
option.OrderActionsBy(o => o.RelativePath);
if (File.Exists(xmlPath))
{
option.IncludeXmlComments(xmlPath, true);
}
if (File.Exists(modelXml))
{
option.IncludeXmlComments(modelXml, true);
}
option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "请输入 Token格式为 Bearer Token",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
BearerFormat = "JWT",
Scheme = "Bearer"
});
option.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
[]
}
});
option.DocInclusionPredicate((docName, apiDesc) =>
{
if (!apiDesc.TryGetMethodInfo(out var methodInfo))
{
return false;
}
var groupName = methodInfo.DeclaringType?
.GetCustomAttributes(true)
.OfType<ApiExplorerSettingsAttribute>()
.FirstOrDefault()?
.GroupName;
return groupName == docName;
});
});
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
var version = ApiVersionEnum.Wechat;
c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{version.GetDescription()}接口");
c.DocExpansion(DocExpansion.None);
});
app.UseServiceContext();
app.UseHttpsRedirection();
app.UseCors("AllowAll");
app.UseMiddleware<GlobalExceptionMiddleware>();
app.UseMiddleware<OperationLogMiddleware>();
app.UseMiddleware<JwtAutoRefreshMiddleware>();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();

View File

@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:51131",
"sslPort": 44367
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5198",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7253;http://localhost:5198",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="9.1.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="11.0.0" />
<PackageReference Include="BCrypt.Net-Next" Version="4.2.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\QYZH.InteractiveMagazine.IService\QYZH.InteractiveMagazine.IService.csproj" />
<ProjectReference Include="..\QYZH.InteractiveMagazine.Service\QYZH.InteractiveMagazine.Service.csproj" />
<ProjectReference Include="..\QYZH.InteractiveMagazine.Infrastructure\QYZH.InteractiveMagazine.Infrastructure.csproj" />
<ProjectReference Include="..\QYZH.InteractiveMagazine.Common\QYZH.InteractiveMagazine.Common.csproj" />
<ProjectReference Include="..\QYZH.InteractiveMagazine.Models\QYZH.InteractiveMagazine.Models.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,71 @@
{
"ConnectionStrings": {
"DefaultConnection": "server=192.168.20.150;port=13306;database=InteractiveMagazine;user=user;password=n68792bu!y99r905;charset=utf8mb4;"
},
"JwtSettings": {
"Issuer": "QYZH.InteractiveMagazine",
"Audience": "QYZH.InteractiveMagazine",
"SecretKey": "zG7pLqR9xVw2bN8fYtHk3mPc5sA1dF6eUjW4gXhC7vB",
"ExpiryMinutes": 120,
"JwtTokenExpiryDays": 30
},
"RedisSettings": {
"ConnectionString": "192.168.20.150:16379,defaultDatabase=5",
"Sentinels": [],
"ExpireSecondRange": [ 3600, 7200 ]
},
"RabbitMq": {
"HostName": "192.168.20.150",
"Port": 5672,
"UserName": "smartschool",
"Password": "@ss%&*otz%d*pq2S",
"VirtualHost": "InteractiveMagazine",
"ClientProvidedName": "Custom connection name"
},
"WeChatSettings": {
"AppId": "wx8c08da60bd207e64",
"AppSecret": "76fa314c34762c0347bd613e488c6872"
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "logs/log-.txt",
"rollingInterval": "Day"
}
}
]
},
"AllowedHosts": "*",
"AiChat": {
"ApiKey": "Ollama",
"BaseUrl": "http://172.16.10.130:11434/v1/",
"Model": "qwen2.5vl:7b",
"TimeoutSeconds": 300,
"MaxTokens": 2000,
"Temperature": 0.5
},
"AliyunOSSConfigs": {
"AccessKeyID": "LTAI5tEBXGewpHSLiSxyx6Bf",
"AccessKeySecret": "w29b8wkw6XQVL8GWXgp3ZesgYeDKvf",
"VodBucketName": "outin-5277bbb52bec11f08dbd00163e169e2b.oss-cn-beijing.aliyuncs.com",
"BucketName": "qyzh2025test",
"Region": "beijing",
"RoleArn": "acs:ram::1064745380176636:role/aliyunosstokengeneratorrole",
"DurationSeconds": 3600, //过期时间(秒)
"Endpoint": "oss-cn-beijing.aliyuncs.com",
"ProjectName": "InteractiveMagazine",
"Domain": "http://oss-test.qyzhjy.com/"
}
}

View File

@ -0,0 +1,43 @@
{
"MedalRuleConfig": {
"Tables": [
{
"TableName": "CheckInRecord",
"DisplayName": "签到记录",
"Fields": [
{ "FieldName": "ContinuousDays", "DisplayName": "连续天数" }
]
},
{
"TableName": "UserPet",
"DisplayName": "宠物",
"Fields": [
{
"FieldName": "GrowthPoints",
"DisplayName": "成长值"
},
{
"FieldName": "FeedingCount",
"DisplayName": "喂养次数"
},
{
"FieldName": "Comprehension",
"DisplayName": "理解力"
},
{
"FieldName": "Judgment",
"DisplayName": "判断力"
},
{
"FieldName": "Expression",
"DisplayName": "表达力"
},
{
"FieldName": "Persuasiveness",
"DisplayName": "说服力"
}
]
}
]
}
}