1
heshaofeng
2026-03-19 c866c43016f12a9db80095d9e5fd7bbc3dcaeb66
1
已添加4个文件
已删除1个文件
已修改15个文件
731 ■■■■■ 文件已修改
项目代码/WIDESEA_WMSClient/src/router/index.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/ChangePassword.vue 334 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/Login.vue 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/inbound/inboundOrder.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/record/stockQuantityChangeRecord.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_BasicService/PasswordPolicyConfigService.cs 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Core/Extensions/SqlsugarSetup.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Core/HttpContextUser/AspNetUser.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_IBasicService/IPasswordPolicyConfigService.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_ISystemService/ISys_UserService.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_InboundService/InboundOrderService.cs 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/Config/PasswordPolicyConfig.cs 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/System/Sys_User.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderService.cs 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_SystemService/Sys_UserService.cs 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_SystemService/WIDESEA_SystemService.csproj 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/System/Sys_UserController.cs 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/接口文档/~$S接口对接文档_V1.04.doc 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/router/index.js
@@ -38,6 +38,14 @@
      }
  }, 
  {
    path: '/change-password',
    name: 'ChangePassword',
    component: () => import('@/views/ChangePassword.vue'),
    meta:{
        anonymous:true
      }
  },
  {
    path: '/bigdata',
    name: 'bigdata',
    component: () => import('@/views/charts/bigdata.vue'),
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/ChangePassword.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,334 @@
<template>
  <div class="login-container">
    <div class="project-name">WMS</div>
    <div class="login-form">
      <div class="form-user" @keypress="changePwdPress">
        <div class="login-text">
          <div>修改密码</div>
        </div>
        <div class="login-text-small">CHANGE PASSWORD</div>
        <!-- æ–°å¢žï¼šç”¨æˆ·åè¾“入框 -->
        <div class="item">
          <div class="input-icon el-icon-user"></div>
          <input
            type="text"
            v-focus
            v-model="pwdForm.userName"
            placeholder="请输入用户名"
          />
        </div>
        <!-- æ—§å¯†ç  -->
        <div class="item">
          <div class="input-icon el-icon-lock"></div>
          <input
            type="password"
            v-model="pwdForm.oldPwd"
            placeholder="请输入旧密码"
          />
        </div>
        <!-- æ–°å¯†ç  -->
        <div class="item">
          <div class="input-icon el-icon-key"></div>
          <input
            type="password"
            v-model="pwdForm.newPwd"
            placeholder="请输入新密码"
          />
        </div>
        <!-- ç¡®è®¤æ–°å¯†ç  -->
        <div class="item">
          <div class="input-icon el-icon-key"></div>
          <input
            type="password"
            v-model="pwdForm.confirmPassword"
            placeholder="请确认新密码"
          />
        </div>
      </div>
      <!-- æäº¤æŒ‰é’® -->
      <div class="loging-btn">
        <el-button
          size="large"
          :loading="loading"
          color="#3a6cd1"
          :dark="true"
          @click="changePassword"
          long
        >
          <span v-if="!loading">确认修改</span>
          <span v-else>正在修改...</span>
        </el-button>
      </div>
      <!-- è¿”回按钮 -->
      <div class="back-login-btn">
        <el-button
          size="large"
          type="text"
          @click="goBack"
        >
          è¿”回
        </el-button>
      </div>
    </div>
    <img class="login-bg" src="/static/login_bg.png" />
  </div>
</template>
<script>
import {
  defineComponent,
  ref,
  reactive,
  getCurrentInstance,
} from "vue";
import { useRouter, useRoute } from "vue-router";
import store from "../store/index";
import http from "@/../src/api/http.js";
export default defineComponent({
  setup(props, context) {
    const loading = ref(false);
    const router = useRouter();
    // å¯†ç è¡¨å•(保留原有默认值,新增用户名双向绑定)
    const pwdForm = reactive({
      userName: store.state.userInfo?.userName || "", // ä»ä¿ç•™ä»Žstore取值,可手动修改
      oldPwd: "",
      newPwd: "",
      confirmPassword: "",
    });
    // å…¨å±€æç¤º
    let appContext = getCurrentInstance().appContext;
    let $message = appContext.config.globalProperties.$message;
    // å›žè½¦è§¦å‘
    const changePwdPress = (e) => {
      if (e.keyCode === 13) {
        changePassword();
      }
    };
    // è¿”回上一页
    const goBack = () => {
      router.go(-1);
    };
    // è¡¨å•校验(新增:用户名非空校验)
    const validateForm = () => {
      if (!pwdForm.userName) return $message.error("请输入用户名"); // æ–°å¢ž
      if (!pwdForm.oldPwd) return $message.error("请输入旧密码");
      if (!pwdForm.newPwd) return $message.error("请输入新密码");
      if (pwdForm.newPwd.length < 6) {
         $message.error("新密码长度不能小于6位");
        return false;
      }
      if (pwdForm.newPwd !== pwdForm.confirmPassword) {
        $message.error("两次输入的新密码不一致");
        return false;
      }
      if (pwdForm.oldPwd === pwdForm.newPwd) {
        $message.error("新密码不能与旧密码相同");
        return false;
      }
      return true;
    };
    // ä¿®æ”¹å¯†ç ï¼ˆé€»è¾‘不变,userName已在表单中)
    const changePassword = () => {
      if (!validateForm()) return;
      loading.value = true;
      http.post("/api/User/ModifyUserNamePwd", {
        userName: pwdForm.userName,
        newPwd: pwdForm.newPwd,
        oldPwd: pwdForm.oldPwd
      }, "正在修改密码....").then((result) => {
        loading.value = false;
        if (!result.status) return $message.error(result.message);
        $message.success("密码修改成功,请重新登录");
        store.commit("clearUserInfo", "");
        setTimeout(() => router.push("/login"), 1500);
      }).catch((err) => {
        loading.value = false;
        $message.error("修改密码失败,请联系管理员");
      });
    };
    return {
      loading,
      pwdForm,
      changePassword,
      changePwdPress,
      goBack,
    };
  },
  directives: {
    focus: {
      inserted: function (el) {
        el.focus();
      },
    },
  },
});
</script>
<style lang="less" scoped>
// å®Œå…¨å¤ç”¨ä½ ç™»å½•页的样式,只新增少量按钮样式
.login-container {
  display: flex;
  width: 100%;
  height: 100%;
  background: rgb(246, 247, 252);
  justify-content: flex-end;
  align-items: center;
}
.login-form {
  align-items: center;
  width: 50%;
  display: flex;
  flex-direction: column;
  z-index: 999;
  .form-user {
    .item {
      border-radius: 5px;
      border: 1px solid #ececec;
      display: flex;
      margin-bottom: 30px;
      background: #ffff;
      height: 45px;
      padding-left: 20px;
      align-items: center;
      .input-icon {
        color: #7a7a7a;
        padding-right: 20px;
        display: flex;
        align-items: center;
      }
    }
  }
  input:-webkit-autofill {
    box-shadow: 0 0 0px 1000px white inset;
    -webkit-box-shadow: 0 0 0px 1000px white inset !important;
  }
  input {
    background: white;
    display: block;
    box-sizing: border-box;
    width: 100%;
    min-width: 0;
    margin: 0;
    padding: 0;
    color: #323233;
    text-align: left;
    border: 0;
    outline: none;
    font-size: 16px;
    height: 100%;
    line-height: normal;
  }
}
.form-user,
.loging-btn {
  width: 400px;
}
.loging-btn {
  box-shadow: 2px 4px 11px #a4c2ff;
  margin-top: 10px;
  button {
    padding: 21px;
    font-size: 14px !important;
    width: 100%;
  }
}
// ä»…新增:返回按钮样式
.back-login-btn {
  width: 400px;
  margin-top: 15px;
  text-align: center;
  button {
    font-size: 13px;
    color: #3a6cd1;
    padding: 0;
    &:hover {
      color: #1850c1;
    }
  }
}
.login-text {
  font-weight: bolder;
  font-size: 20px;
  letter-spacing: 2px;
  position: relative;
  display: flex;
}
.login-text-small {
  margin-bottom: 20px;
  font-size: 13px;
  color: #7d7c7c;
}
.login-bg {
  left: 0;
  position: absolute;
  height: 100%;
  width: 50%;
  z-index: 0;
}
.project-name {
  position: absolute;
  top: 40px;
  left: 40px;
  z-index: 9999;
  font-weight: bolder;
  background-image: linear-gradient(to right, #1850c1, #9c009c);
  -webkit-background-clip: text;
  color: transparent;
  font-size: 25px;
}
// å“åº”式适配(复用你的逻辑)
@media screen and (max-width: 700px) {
  .login-bg,
  .project-name {
    display: none;
  }
  .login-container {
    padding: 2rem;
    justify-content: center;
  }
  .login-form {
    width: 100%;
  }
  .form-user,
  .loging-btn,
  .back-login-btn {
    width: 100%;
  }
}
</style>
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/Login.vue
@@ -26,14 +26,17 @@
            <el-option v-for="item in stationOptions" :key="item.value" :label="item.label" :value="item.value" />
          </el-select>
        </div>
      </div>
      <div class="loging-btn">
        <el-button size="large" :loading="loading" color="#3a6cd1" :dark="true" @click="login" long>
          <span v-if="!loading">登录</span>
          <span v-else>正在登录...</span>
        </el-button>
      </div>
      <!-- æ–°å¢žï¼šä¿®æ”¹å¯†ç æŒ‰é’®ï¼ˆæ ·å¼ä¸Žç™»å½•页统一) -->
      <div class="change-pwd-btn">
        <el-button size="large" type="text" @click="goChangePassword" long>
          <span style="color: #3a6cd1; font-size: 13px;">修改密码</span>
        </el-button>
      </div>
    </div>
@@ -104,6 +107,11 @@
    let $message = appContext.config.globalProperties.$message;
    let router = useRouter();
    // æ–°å¢žï¼šè·³è½¬åˆ°ä¿®æ”¹å¯†ç é¡µé¢
    const goChangePassword = () => {
      router.push({ path: "/change-password" });
    };
    const login = () => {
      if (!userInfo.userName) return $message.error("请输入用户名");
      if (!userInfo.password) return $message.error("请输入密码");
@@ -122,7 +130,7 @@
          getVierificationCode();
          return $message.error(result.message);
        }
        $message.success("登录成功,正在跳转!");
        $message.success(result.message);
        store.commit("setUserInfo", result.data);
        router.push({ path: "/" });
@@ -147,6 +155,7 @@
      stationOptions,
      stationValue,
      handleStationChange,
      goChangePassword // æ–°å¢žï¼šæš´éœ²è·³è½¬æ–¹æ³•
    };
  },
  directives: {
@@ -264,6 +273,22 @@
    padding: 21px;
    font-size: 14px !important;
    width: 100%;
  }
}
// æ–°å¢žï¼šä¿®æ”¹å¯†ç æŒ‰é’®æ ·å¼ï¼ˆä¸Žç™»å½•页风格统一)
.change-pwd-btn {
  width: 400px;
  margin-top: 15px;
  text-align: center;
  button {
    font-size: 13px;
    padding: 0;
    &:hover {
      color: #1850c1 !important;
    }
  }
}
@@ -517,7 +542,8 @@
  }
  .form-user,
  .loging-btn {
  .loging-btn,
  .change-pwd-btn { // æ–°å¢žï¼šå“åº”式适配修改密码按钮
    width: 100%;
  }
}
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/inbound/inboundOrder.vue
@@ -23,6 +23,9 @@
      inboundOrderNo: "",
      upperOrderNo: "",
      remark: "",
      orderStatus: "",
      warehouseId: "",
      supplierId: "",
    });
    const editFormOptions = ref([
      [
@@ -33,7 +36,6 @@
          type: "select",
          dataKey: "inOrderType",
          data: [],
          hidden: true
        },
        {
          field: "inboundOrderNo",
@@ -326,6 +328,8 @@
          type: "string",
          width: 90,
          align: "left",
          edit: { type: "" },
          required: true,
        },
        {
          field: "orderDetailStatus",
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/record/stockQuantityChangeRecord.vue
@@ -41,6 +41,7 @@
        { title: "出库原条码", field: "originalSerilNumber", type: "like" },
        { title: "拆包新条码", field: "newSerilNumber", type: "like" },
        { title: "批次号", field: "batchNo", type: "like" },
        { title: "仓库号", field: "warehouseCode", type: "like" },
      ],
    ]);
    const columns = ref([
@@ -163,6 +164,13 @@
        align: "left",
      },
      {
        field: "warehouseCode",
        title: "仓库",
        type: "string",
        width: 100,
        align: "left",
      },
      {
        field: "creater",
        title: "创建人",
        type: "string",
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_BasicService/PasswordPolicyConfigService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
using WIDESEA_Core.Helper;
using WIDESEA_IBasicService;
using WIDESEA_Model.Models.Config;
namespace WIDESEA_BasicService
{
    public class PasswordPolicyConfigService : ServiceBase<PasswordPolicyConfig, IRepository<PasswordPolicyConfig>>, IPasswordPolicyConfigService
    {
        public PasswordPolicyConfigService(IRepository<PasswordPolicyConfig> BaseDal) : base(BaseDal)
        {
        }
        /// <summary>
        /// èŽ·å–å¯†ç ç­–ç•¥é…ç½®ï¼ˆä»ŽAppSettings读取)
        /// </summary>
        public PasswordPolicyConfig GetConfigValue(string key = "")
        {
            try
            {
                // ä»Žappsettings.json的PasswordPolicy节点读取配置
                var config = new PasswordPolicyConfig
                {
                    EnablePasswordExpire = AppSettings.Get(new[] { "PasswordPolicy", "EnablePasswordExpire" }).ObjToBool(),
                    PasswordExpireDays = AppSettings.Get(new[] { "PasswordPolicy", "PasswordExpireDays" }).ObjToInt(90),
                    RemindBeforeExpireDays = AppSettings.Get(new[] { "PasswordPolicy", "RemindBeforeExpireDays" }).ObjToInt(7)
                };
                return config;
            }
            catch
            {
                // è¯»å–失败返回默认配置
                return new PasswordPolicyConfig();
            }
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Core/Extensions/SqlsugarSetup.cs
@@ -1,4 +1,4 @@
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using SqlSugar;
using StackExchange.Profiling;
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Core/HttpContextUser/AspNetUser.cs
@@ -184,5 +184,7 @@
        public string UserTrueName { get; set; }
        public string HeadImageUrl { get; set; }
        public DateTime? PwdLastModifyTime { get; set; }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_IBasicService/IPasswordPolicyConfigService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WIDESEA_Core.BaseServices;
using WIDESEA_Model.Models.Config;
namespace WIDESEA_IBasicService
{
    public interface IPasswordPolicyConfigService : IService<PasswordPolicyConfig>
    {
        /// <summary>
        /// èŽ·å–å¯†ç ç­–ç•¥é…ç½®
        /// </summary>
        /// <param name="key">预留键值(适配原有方法签名)</param>
        /// <returns>密码策略配置</returns>
        PasswordPolicyConfig GetConfigValue(string key = "");
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_ISystemService/ISys_UserService.cs
@@ -22,5 +22,7 @@
        WebResponseContent ModifyPwd(string oldPwd, string newPwd);
        WebResponseContent ModifyUserPwd(string password, string userName);
        WebResponseContent ModifyUserNamePwd(string oldPassword, string userName,string newPassword);
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_InboundService/InboundOrderService.cs
@@ -705,9 +705,10 @@
            {
                _unitOfWorkManage.BeginTran();
                // æ™ºèƒ½è¯†åˆ«è¾“入类型
                string palletCode = null;
                string barcode = null;
                int stockStatus = 0;
                // 1. å…ˆå°è¯•按托盘号查询
                var stockByPallet = _stockRepository.Db.Queryable<Dt_StockInfo>()
@@ -720,10 +721,17 @@
                {
                    // è¯†åˆ«ä¸ºæ‰˜ç›˜å·
                    palletCode = code;
                    var task =_taskRepository.Db.Queryable<Dt_Task>().Where(t => t.PalletCode == palletCode).ToList();
                    if(task!=null && task.Any())
                    stockStatus = stockByPallet.StockStatus;
                    var task = _taskRepository.Db.Queryable<Dt_Task>().Where(t => t.PalletCode == palletCode).ToList();
                    if (task != null && task.Any())
                    {
                        return WebResponseContent.Instance.Error($"托盘号 {palletCode} å­˜åœ¨æœªå®Œæˆçš„任务,无法撤销");
                    }
                    if (stockStatus == StockStatusEmun.入库确认.ObjToInt())
                    {
                        return WebResponseContent.Instance.Error($"托盘号 {palletCode} å¤„于入库确认状态,禁止整托盘撤销,请单独撤销条码");
                    }
                }
                else
@@ -734,13 +742,14 @@
                    {
                        var stockInfo = _stockRepository
                            .Db.Queryable<Dt_StockInfo>()
                            .Where(s => s.Id == detail.StockId&& (s.StockStatus == (int)StockStatusEmun.组盘暂存 || s.StockStatus == StockStatusEmun.入库确认.ObjToInt()))
                            .Where(s => s.Id == detail.StockId && (s.StockStatus == (int)StockStatusEmun.组盘暂存 || s.StockStatus == StockStatusEmun.入库确认.ObjToInt()))
                            .First();
                        if (stockInfo != null)
                        {
                            barcode = code;
                            palletCode = stockInfo.PalletCode;
                            stockStatus = stockInfo.StockStatus;
                        }
                    }
                    else
@@ -773,6 +782,16 @@
                        return WebResponseContent.Instance.Error($"托盘 {palletCode} ä¸‹æœªæ‰¾åˆ°æ¡ç  {barcode} çš„æ˜Žç»†è®°å½•");
                    }
                    if (stockStatus == StockStatusEmun.入库确认.ObjToInt())
                    {
                        var totalDetails = stock.Details?.Count ?? 0;
                        if (totalDetails <= 1)
                        {
                            _unitOfWorkManage.RollbackTran();
                            return WebResponseContent.Instance.Error($"托盘 {palletCode} å¤„于入库确认状态,当前仅剩余最后1条明细,禁止撤销(必须保留至少1条库存明细)");
                        }
                    }
                    ResetInboundOrderStatus(new List<string> { targetDetail.OrderNo }, new List<string> { targetDetail.Barcode });
                    _stockDetailRepository.DeleteData(targetDetail);
@@ -782,10 +801,13 @@
                    if (!remainingDetails.Any())
                    {
                        ResetInboundOrderStatus(stock.Details.Select(d => d.OrderNo).Distinct().ToList());
                        _stockRepository.DeleteData(stock);
                        if (stockStatus == (int)StockStatusEmun.组盘暂存)
                        {
                            ResetInboundOrderStatus(stock.Details.Select(d => d.OrderNo).Distinct().ToList());
                            _stockRepository.DeleteData(stock);
                        }
                        _unitOfWorkManage.CommitTran();
                        return WebResponseContent.Instance.OK($"条码 {barcode} æ’¤é”€æˆåŠŸï¼Œæ‰˜ç›˜æ— å‰©ä½™æ˜Žç»†ï¼Œå·²åˆ é™¤æ‰˜ç›˜å¹¶é‡ç½®å…³è”å…¥åº“å•çŠ¶æ€");
                        return WebResponseContent.Instance.OK($"条码 {barcode} æ’¤é”€æˆåŠŸï¼Œæ‰˜ç›˜æ— å‰©ä½™æ˜Žç»†ï¼Œå·²é‡ç½®å…³è”å…¥åº“å•çŠ¶æ€");
                    }
                    _unitOfWorkManage.CommitTran();
@@ -793,7 +815,6 @@
                }
                else
                {
                    // ===== æ’¤é”€æ•´ä¸ªæ‰˜ç›˜ =====
                    var stock = _stockRepository.Db.Queryable<Dt_StockInfo>()
                        .Includes(o => o.Details)
                        .First(x => x.PalletCode == palletCode
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Config/PasswordPolicyConfig.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WIDESEA_Model.Models.Config
{
    public class PasswordPolicyConfig
    {
        /// <summary>
        /// æ˜¯å¦å¯ç”¨å¯†ç è¿‡æœŸç­–ç•¥
        /// </summary>
        public bool EnablePasswordExpire { get; set; } = true;
        /// <summary>
        /// å¯†ç æœ‰æ•ˆæœŸï¼ˆå¤©ï¼‰
        /// </summary>
        public int PasswordExpireDays { get; set; } = 90;
        /// <summary>
        /// æå‰æé†’天数
        /// </summary>
        public int RemindBeforeExpireDays { get; set; } = 7;
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/System/Sys_User.cs
@@ -103,7 +103,7 @@
        /// <summary>
        /// æœ€åŽå¯†ç ä¿®æ”¹æ—¶é—´
        /// </summary>
        [SugarColumn(IsNullable = true, IsOnlyIgnoreInsert = true, ColumnDescription = "最后密码修改时间")]
        [SugarColumn(IsNullable = true, ColumnDescription = "最后密码修改时间")]
        public DateTime? LastModifyPwdDate { get; set; }
        /// <summary>
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderService.cs
@@ -146,24 +146,24 @@
                            BarcodeQty = item.OrderQuantity,
                            BarcodeUnit = item.Unit,
                        };
                        var issueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode, item.BarcodeUnit, item.BarcodeQty);
                        outboundOrderDetail.Unit = issueoStockResult.Unit;
                        outboundOrderDetail.OrderQuantity = issueoStockResult.Quantity;
                        var moveissueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode, item.BarcodeUnit, item.BarcodeMoveQty);
                        outboundOrderDetail.MoveQty = moveissueoStockResult.Quantity;
                        var unitConvertResult = await ConvertUnitAsync(item.MaterielCode, item.BarcodeUnit, item.BarcodeQty, item.BarcodeMoveQty);
                        outboundOrderDetail.Unit = unitConvertResult.Unit;
                        outboundOrderDetail.OrderQuantity = unitConvertResult.OrderQuantity;
                        outboundOrderDetail.MoveQty = unitConvertResult.MoveQty;
                        outboundOrderDetail.MaterielName = materielInfos.FirstOrDefault(x => x.MaterielCode == item.MaterielCode)?.MaterielName ?? "";
                        outboundOrderDetails.Add(outboundOrderDetail);
                    }
                    else
                    {
                        var unitConvertResult = await ConvertUnitAsync(item.MaterielCode, item.BarcodeUnit, item.BarcodeQty, item.BarcodeMoveQty);
                        #region é”å®šçŠ¶æ€ä¸‹éžæ•°é‡å­—æ®µä¸€è‡´æ€§æ ¡éªŒ
                        if (outboundOrderDetail.LockQuantity != 0)
                        {
                            var isFieldChanged = !string.Equals(outboundOrderDetail.MaterielCode, item.MaterielCode)
                                || !string.Equals(outboundOrderDetail.SupplyCode, item.SupplyCode)
                                || !string.Equals(outboundOrderDetail.BatchNo, item.BatchNo)
                                || !string.Equals(outboundOrderDetail.Unit, item.Unit)
                                || !string.Equals(outboundOrderDetail.Unit, unitConvertResult.Unit)
                                || !string.Equals(outboundOrderDetail.WarehouseCode, item.WarehouseCode)
                                || !string.Equals(outboundOrderDetail.lineNo, item.lineNo)
                                ;
@@ -203,12 +203,9 @@
                            outboundOrderDetail.BarcodeMoveQty = item.MoveQty;
                            outboundOrderDetail.BarcodeQty = item.OrderQuantity;
                            outboundOrderDetail.BarcodeUnit = item.Unit;
                            var issueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode, item.BarcodeUnit, item.BarcodeQty);
                            outboundOrderDetail.Unit = issueoStockResult.Unit;
                            outboundOrderDetail.OrderQuantity = issueoStockResult.Quantity;
                            var moveissueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode, item.BarcodeUnit, item.BarcodeMoveQty);
                            outboundOrderDetail.MoveQty = moveissueoStockResult.Quantity;
                            outboundOrderDetail.Unit = unitConvertResult.Unit;
                            outboundOrderDetail.OrderQuantity = unitConvertResult.OrderQuantity;
                            outboundOrderDetail.MoveQty = unitConvertResult.MoveQty;
                        }
                        else
                        {
@@ -510,5 +507,26 @@
            return new PageGridData<Dt_OutboundOrder>(totalCount, data);
        }
        private async Task<UnitConvertResult> ConvertUnitAsync(string materielCode, string barcodeUnit, decimal barcodeQty, decimal barcodeMoveQty)
        {
            var issueoStockResult = await _materialUnitService.ConvertFromToStockAsync(materielCode, barcodeUnit, barcodeQty);
            var moveissueoStockResult = await _materialUnitService.ConvertFromToStockAsync(materielCode, barcodeUnit, barcodeMoveQty);
            return new UnitConvertResult
            {
                Unit = issueoStockResult.Unit,
                OrderQuantity = issueoStockResult.Quantity,
                MoveQty = moveissueoStockResult.Quantity
            };
        }
        private class UnitConvertResult
        {
            public string Unit { get; set; }
            public decimal OrderQuantity { get; set; }
            public decimal MoveQty { get; set; }
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs
@@ -1988,6 +1988,7 @@
                Creater = stockDetail.Creater,
                CreateDate = stockDetail.CreateDate,
                WarehouseCode = stockDetail.WarehouseCode,
                ValidDate = stockDetail.ValidDate,
                Remark = $"出库完成删除,条码:{request.Barcode},原数量:{stockDetail.StockQuantity},出库数量:{actualOutboundQuantity},操作者:{request.Operator}"
            };
            _stockDetailHistoryRepository.AddData(historyRecord);
@@ -2485,11 +2486,14 @@
                    x.Barcode == request.Barcode &&
                 
                    (x.OperateType == "出库完成" || x.OperateType == "拆包-原始记录"));
                if (historyDetail == null)
                if(historyDetail != null)
                {
                    response.Success = false;
                    response.Message = $"条码 {request.Barcode} å·²æ‹†åŒ…,无法撤销";
                    return WebResponseContent.Instance.Error(response.Message);
                    double minutesDiff = (DateTime.Now - historyDetail.InsertTime).TotalMinutes;
                    if (minutesDiff >= 30)
                    {
                        return WebResponseContent.Instance.Error($"条码{request.Barcode}已无法撤销");
                    }
                }
                Dt_OutStockLockInfo lockInfo = _outboundLockInfoRepository.QueryFirst(x =>
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_SystemService/Sys_UserService.cs
@@ -18,6 +18,8 @@
using MailKit.Search;
using OrderByType = SqlSugar.OrderByType;
using System.Drawing.Printing;
using WIDESEA_Model.Models.Config;
using WIDESEA_IBasicService;
//using WIDESEA_Core.HostedService;
namespace WIDESEA_SystemService
@@ -28,24 +30,29 @@
        private readonly ICacheService _cacheService;
        private readonly ISys_MenuService _menuService;
        private readonly ISys_RoleService _roleService;
        private readonly IPasswordPolicyConfigService _passwordPolicyConfigService;
        public IRepository<Sys_User> Repository => BaseDal;
        public Sys_UserService(IRepository<Sys_User> repository, IUnitOfWorkManage unitOfWorkManage, ICacheService cacheService, ISys_MenuService menuService, ISys_RoleService roleService) : base(repository)
        public Sys_UserService(IRepository<Sys_User> repository, IUnitOfWorkManage unitOfWorkManage, ICacheService cacheService, ISys_MenuService menuService, ISys_RoleService roleService, IPasswordPolicyConfigService passwordPolicyConfigService) : base(repository)
        {
            _unitOfWorkManage = unitOfWorkManage;
            _cacheService = cacheService;
            _menuService = menuService;
            _roleService = roleService;
            _passwordPolicyConfigService = passwordPolicyConfigService;
        }
        public WebResponseContent Login(LoginInfo loginInfo)
        {
            WebResponseContent content = new WebResponseContent();
            PasswordPolicyConfig passwordPolicy = null;
            string token = null;
            try
            {
                //BaseDal.QueryFirst(x => x.UserName == loginInfo.UserName);
                passwordPolicy = _passwordPolicyConfigService.GetConfigValue("") ?? new PasswordPolicyConfig();
                string msg = string.Empty;
                #region ä¸´æ—¶ä½¿ç”¨
@@ -59,9 +66,51 @@
                }
                #endregion
                UserInfo user = BaseDal.QueryFirst(x => x.UserName == loginInfo.UserName && x.UserPwd == loginInfo.Password, x => new UserInfo { HeadImageUrl = x.HeadImageUrl, RoleId = x.RoleId, TenantId = x.TenantId, UserId = x.UserId, UserName = x.UserName, UserTrueName = x.UserTrueName });
                UserInfo user = BaseDal.QueryFirst(x => x.UserName == loginInfo.UserName && x.UserPwd == loginInfo.Password, x => new UserInfo { HeadImageUrl = x.HeadImageUrl, RoleId = x.RoleId, TenantId = x.TenantId, UserId = x.UserId, UserName = x.UserName, UserTrueName = x.UserTrueName,PwdLastModifyTime = x.LastModifyPwdDate });
                if (user != null)
                {
                    // 3. å¯†ç è¿‡æœŸç­–略检查(仅启用时执行)
                    if (passwordPolicy.EnablePasswordExpire)
                    {
                        DateTime pwdModifyTime = user.PwdLastModifyTime ?? DateTime.Now.AddYears(-1);
                        TimeSpan passwordAge = DateTime.Now - pwdModifyTime;
                        int daysToExpire = passwordPolicy.PasswordExpireDays - (int)passwordAge.TotalDays;
                        // å¯†ç å·²è¿‡æœŸï¼Œå¼ºåˆ¶æ”¹å¯†
                        if (daysToExpire <= 0)
                        {
                            return WebResponseContent.Instance.Error(
                                "您的密码已过期,请先修改密码后再登录",
                                data: new { needChangePwd = true, userId = user.UserId });
                        }
                        // å¯†ç å³å°†è¿‡æœŸï¼Œç™»å½•成功并提醒
                        if (daysToExpire <= passwordPolicy.RemindBeforeExpireDays)
                        {
                            token = JwtHelper.IssueJwt(new TokenModelJwt()
                            {
                                UserId = user.UserId,
                                RoleId = user.RoleId,
                                UserName = user.UserName,
                                TenantId = user.TenantId,
                            });
                            App.User.UpdateToke(token, user.UserId);
                            content = WebResponseContent.Instance.OK(
                                message: $"您的密码将在{daysToExpire}天后过期,请及时修改",
                                data: new
                                {
                                    token,
                                    userName = user.UserName,
                                    img = user.HeadImageUrl,
                                    UserTrueName = user.UserTrueName,
                                    needChangePwd = false,
                                    pwdWillExpire = true,
                                    daysToExpire = daysToExpire
                                });
                            return content;
                        }
                    }
                    object obj = _menuService.GetMenuActionList(user.RoleId);
                    if (obj is not IEnumerable<object> list)
                    {
@@ -72,7 +121,7 @@
                        return WebResponseContent.Instance.Error("无登录权限");
                    }
                    string token = JwtHelper.IssueJwt(new TokenModelJwt()
                     token = JwtHelper.IssueJwt(new TokenModelJwt()
                    {
                        UserId = user.UserId,
                        RoleId = user.RoleId,
@@ -84,7 +133,12 @@
                    //if (PermissionDataHostService.UserRoles.FirstOrDefault(x => x.UserId == user.UserId) == null)
                    //    PermissionDataHostService.UserRoles.AddRange(PermissionDataHostService.GetUserRoles(Db, user.UserId));
                    content = WebResponseContent.Instance.OK(data: new { token, userName = user.UserName, img = user.HeadImageUrl, user.UserTrueName });
                    content = WebResponseContent.Instance.OK(message: "登入成功,正在跳转页面", data: new { token, userName = user.UserName, img = user.HeadImageUrl, user.UserTrueName, needChangePwd = false,
                        pwdWillExpire = false,
                        daysToExpire = passwordPolicy.EnablePasswordExpire
                    ? passwordPolicy.PasswordExpireDays - (int)(DateTime.Now - (user.PwdLastModifyTime ?? DateTime.Now)).TotalDays
                    : 0
                    });
                }
                else
                {
@@ -151,6 +205,9 @@
            string pwd = "123456";
            string uesrName = saveModel.MainData[nameof(Sys_User.UserName).FirstLetterToLower()].ToString();
            saveModel.MainData[nameof(Sys_User.UserPwd).FirstLetterToLower()] = pwd.EncryptDES(AppSecret.User);
            string pwdModifyTimeField = nameof(Sys_User.LastModifyPwdDate).FirstLetterToLower();
            saveModel.MainData[pwdModifyTimeField] = DateTime.Now;
            WebResponseContent content = base.AddData(saveModel);
            if (content.Status)
@@ -244,6 +301,7 @@
                Sys_User user = BaseDal.QueryFirst(x => x.UserName == userName);
                if (user == null) return WebResponseContent.Instance.Error("用户不存在");
                user.UserPwd = password.EncryptDES(AppSecret.User);
                user.LastModifyPwdDate = DateTime.Now;
                BaseDal.UpdateData(user);
                if (App.User.UserId == user.UserId)
                {
@@ -265,5 +323,63 @@
            }
            return content;
        }
        public WebResponseContent ModifyUserNamePwd(string userName,string oldPwd, string password)
        {
            WebResponseContent content = new WebResponseContent();
            string message = "";
            // åŽ»é™¤é¦–å°¾ç©ºæ ¼ï¼Œç©ºå€¼å¤„ç†
            oldPwd = oldPwd?.Trim();
            password = password?.Trim();
            userName = userName?.Trim();
            try
            {
                // 1. åŸºç¡€å‚数校验
                if (string.IsNullOrEmpty(userName)) return WebResponseContent.Instance.Error("用户名不能为空");
                if (string.IsNullOrEmpty(oldPwd)) return WebResponseContent.Instance.Error("旧密码不能为空");
                if (string.IsNullOrEmpty(password)) return WebResponseContent.Instance.Error("新密码不能为空");
                if (oldPwd == password) return WebResponseContent.Instance.Error("新密码不能与旧密码相同");
                if (password.Length < 6) return WebResponseContent.Instance.Error("新密码长度不能少于6位");
                // 2. èŽ·å–ç”¨æˆ·ä¿¡æ¯
                Sys_User user = BaseDal.QueryFirst(x => x.UserName == userName);
                if (user == null) return WebResponseContent.Instance.Error("用户不存在");
                // 3. æ ¡éªŒæ—§å¯†ç ï¼ˆè§£å¯†åŽå¯¹æ¯”)
                string decryptedOldPwd = user.UserPwd.DecryptDES(AppSecret.User); // è§£å¯†æ•°æ®åº“中的密码
                if (decryptedOldPwd != oldPwd) // å¯¹æ¯”原始旧密码
                {
                    return WebResponseContent.Instance.Error("旧密码输入错误");
                }
                // 4. æ›´æ–°å¯†ç åŠç›¸å…³ä¿¡æ¯
                user.UserPwd = password.EncryptDES(AppSecret.User); // åŠ å¯†æ–°å¯†ç 
                user.LastModifyPwdDate = DateTime.Now;
                BaseDal.UpdateData(user);
                // 5. å¦‚果是当前登录用户,重新生成JWT Token并更新缓存
                if (App.User.UserId == user.UserId)
                {
                    string token = JwtHelper.IssueJwt(new TokenModelJwt()
                    {
                        UserId = user.UserId,
                        RoleId = user.RoleId,
                        UserName = user.UserName,
                        TenantId = user.TenantId,
                    });
                    _cacheService.AddOrUpdate(user.UserId.ToString(), token);
                }
                // 6. è¿”回成功结果
                return content.OK("密码修改成功");
            }
            catch (Exception ex)
            {
                message = ex.Message; // è®°å½•异常信息(建议补充日志框架记录)
                content.Error("服务器出了点问题,请稍后再试");
            }
            return content;
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_SystemService/WIDESEA_SystemService.csproj
@@ -7,6 +7,7 @@
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\WIDESEA_IBasicService\WIDESEA_IBasicService.csproj" />
    <ProjectReference Include="..\WIDESEA_ISystemService\WIDESEA_ISystemService.csproj" />
  </ItemGroup>
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -604,7 +604,7 @@
                        .Where(x => x.OrderNo == stockLockInfo.OrderNo)
                        .Includes(x => x.Details)
                        .First();
                    string Operator = outboundOrder.Modifier;
                    if (outboundOrder != null)
                    {
                        var allocatInfo =_allocateMaterialInfo.Db.Queryable<Dt_AllocateMaterialInfo>().Where(x => x.OrderNo == outboundOrder.OrderNo).ToList();
@@ -629,7 +629,7 @@
                        }
                        // 7. å›žè°ƒMES
                        string Operator = outboundOrder.Modifier;
                        HttpResponseResult<MesResponseDTO> httpResponseResult = new HttpResponseResult<MesResponseDTO>();
                        string reqCode = Guid.NewGuid().ToString();
                        string reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
@@ -1022,8 +1022,8 @@
                    await Db.Deleteable(task).ExecuteCommandAsync();
                }
                Dt_OutboundOrder outboundOrder = _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().Where(x => x.OrderNo == stockInfo.Details.FirstOrDefault().OrderNo).Includes(x=>x.Details).First();
                string Operator = outboundOrder.Modifier;
                if (outboundOrder != null)
                {
                    foreach (var item in stockInfo.Details.Where(x => x.OrderNo == outboundOrder.OrderNo).ToList())
@@ -1045,7 +1045,7 @@
                        _outboundOrderService.UpdateData(outboundOrder);
                    }
                }
                string Operator = outboundOrder.Modifier;
                ///回调MES
                HttpResponseResult<MesResponseDTO> httpResponseResult = new HttpResponseResult<MesResponseDTO>();
                string reqCode = Guid.NewGuid().ToString();
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/System/Sys_UserController.cs
@@ -83,10 +83,16 @@
            return Service.GetCurrentUserInfo();
        }
        [HttpPost, Route("modifyPwd")]
        [HttpPost, Route("modifyPwd"), AllowAnonymous]
        public IActionResult ModifyPwd(string oldPwd, string newPwd)
        {
            return Json(Service.ModifyPwd(oldPwd, newPwd));
        }
        [HttpPost, Route("ModifyUserNamePwd"), AllowAnonymous]
        public IActionResult ModifyUserNamePwd([FromBody] ModifyUserNamePwd modifyUserName)
        {
            return Json(Service.ModifyUserNamePwd(modifyUserName.userName, modifyUserName.oldPwd, modifyUserName.newPwd));
        }
        [HttpGet, Route("getVierificationCode"), AllowAnonymous]
@@ -154,4 +160,11 @@
        public string name { get; set; }
        public string pwd { get; set; }
    }
    public class ModifyUserNamePwd
    {
        public string userName { get; set; }
        public string oldPwd { get; set; }
        public string newPwd { get; set; }
    }
}
ÏîÄ¿×ÊÁÏ/½Ó¿ÚÎĵµ/~$S½Ó¿Ú¶Ô½ÓÎĵµ_V1.04.doc
Binary files differ