分类

类型:
不限 游戏开发 计算机程序开发 Android开发 网站开发 笔记总结 其他
评分:
不限 10 9 8 7 6 5 4 3 2 1
原创:
不限
年份:
不限 2018 2019 2020 2021

技术文章列表

  • 上传资源,获取积分 精华

    上传资源,获取积分“WRITE-BUG技术共享平台”是一个专注于校园计算机技术交流共享的平台,面向的主要目标群体是我们计算机相关专业的大学生。在平台上,大家既可以交流学校课内学习的心得体会,也可以分享自己课外积累的技术经验。
    为了充实平台的资源库,更好地服务于各位同学,平台决定推出“众源计划”,有偿征集同学们自己计算机专业的作业、课程设计或是毕业设计等资源。“众源计划”的主要目的是创建一个具有一定规模的“技术资源库”,资源库里的每一份资源,都必须有详细的开发文档和可编译的源代码。
    作业、课程设计或是毕业设计等资源是同学们自己辛苦付出的成果,也是自己技术进步的见证。这部分资源通常都有详细的开发文档和完整的程序源代码,能够帮助其他初学者更好地消化和吸收将要学习的技术,降低学习门槛。所以,平台决定积分奖励征集这些资源。
    具体要求奖励方式
    资源上传并审核通过后,根据资源质量,奖励每贴 10 - 100 点积分
    上传流程
    会员登录自己的账号上传资源
    资源上传后,管理员会在 24 小时之内审核资源
    审核通过后,管理员会立即发放奖励积分至所对应账户

    审核重点
    重点审核资源是否具有详细的文档和完整的源代码
    审查资源是否原创,切勿重复提交

    资源要求“众源计划”仅对两类资源进行积分奖励征集,分别是“课内资源”和“课外资源”,各类资源具体要求如下所示。

    课内资源

    内容范围:计算机相关专业课内的毕业设计、课程设计、小学期、大作业等课程内开发的程序,程序包括游戏、PC程序、APP、网站或者其他软件形式
    内容要求:资源必须要包括完整的程序源代码和详细的开发文档或报告

    课外资源

    内容范围:计算机相关专业的课外自己主导研究游戏、项目、竞赛、个人研究等,区别于课程设计和毕业设计等课内资源
    内容要求:资源必须要包括完整的程序源代码和详细的开发文档或报告


    使用须知** 在上传资源前,请花两分钟阅读 “使用须知” 部分内容 **
    50 留言 2019-01-24 09:26:15 奖励100点积分
  • 基于以太坊发行的ERC20代币源码

    全网最全ERC20智能合约大全如果你想在以太坊ETH上发行一个ERC20的代币,你需要了解ERC20代币合约,通读本文以后,你将获得一个比较厉害的技能,希望你能学会。下图是基于ETH发行的ERC20版的USDT,你可以大概了解一下一个基本的ERC20代币的一些信息:https://cn.etherscan.com/token/0xdac17f958d2ee523a2206206994597c13d831ec7

    什么是ERC20
    可以把ERC20简单理解成以太坊上的一个代币协议,所有基于以太坊开发的代币合约都遵守这个协议。遵守这些协议的代币我们可以认为是标准化的代币,而标准化带来的好处是兼容性好。这些标准化的代币可以被各种以太坊钱包支持,用于不同的平台和项目。说白了,你要是想在以太坊上发行代币融资,必须要遵守ERC20标准。
    ERC20的标准接口是这样的:
    contract ERC20 { function name() constant returns (string name) function symbol() constant returns (string symbol) function decimals() constant returns (uint8 decimals) function totalSupply() constant returns (uint totalSupply); function balanceOf(address _owner) constant returns (uint balance); function transfer(address _to, uint _value) returns (bool success); function transferFrom(address _from, address _to, uint _value) returns (bool success); function approve(address _spender, uint _value) returns (bool success); function allowance(address _owner, address _spender) constant returns (uint remaining); event Transfer(address indexed _from, address indexed _to, uint _value); event Approval(address indexed _owner, address indexed _spender, uint _value); }下面对上面的内容做一个简单的解释:
    name

    返回ERC20代币的名字,例如”My test token”。

    symbol

    返回代币的简称,例如:MTT,这个也是我们一般在代币交易所看到的名字。

    decimals

    返回token使用的小数点后几位。比如如果设置为3,就是支持0.001表示。

    totalSupply

    返回token的总供应量

    balanceOf

    返回某个地址(账户)的账户余额

    transfer

    从代币合约的调用者地址上转移_value的数量token到的地址_to,并且必须触发Transfer事件。

    transferFrom

    从地址_from发送数量为_value的token到地址_to,必须触发Transfer事件。transferFrom方法用于允许合同代理某人转移token。条件是from账户必须经过了approve。这个后面会举例说明。

    approve

    允许_spender多次取回您的帐户,最高达_value金额。 如果再次调用此函数,它将以_value覆盖当前的余量。

    allowance

    返回_spender仍然被允许从_owner提取的金额。

    后面三个方法不好理解,这里还需要补充说明一下:approve是授权第三方(比如某个服务合约)从发送者账户转移代币,然后通过 transferFrom() 函数来执行具体的转移操作。
    举例说明,如果账户A有1000个ETH,想允许B账户随意调用他的100个ETH,过程如下:
    1. A账户按照以下形式调用approve函数approve(B,100) 2. B账户想用这100个ETH中的10个ETH给C账户,调用transferFrom(A, C, 10) 3. 调用allowance(A, B)可以查看B账户还能够调用A账户多少个token 发行ERC20代币步骤

    在 Chrome 插件商店搜索并安装 MetaMask。

    MetaMask是钱包的一种,在chrome浏览器中,安装MetaMask插件即可,安装完成后,右上角会出现一个“狐狸头”的标志,点击该标志,打开钱包,第一步,创建账户,(创建账户只需要输入面密码即可,名称创建后可以随便改,该账户就是一个hash值,如何给自己创建的账户冲以太币呢,你可以通过在交易所买入一些ETH,然后转入即可)创建成功后,记住密码还有产生的几个随机单词(一定要记录下来)。



    运行后,用它来给我们初始化一个以太坊钱包。


    准备一份ERC20的智能合约源码(仓库里有很多),一个简单的合约如下:
    pragma solidity ^0.4.12;contract IMigrationContract { function migrate(address addr, uint256 nas) returns (bool success);}contract SafeMath { function safeAdd(uint256 x, uint256 y) internal returns(uint256) { uint256 z = x + y; assert((z >= x) && (z >= y)); return z; } function safeSubtract(uint256 x, uint256 y) internal returns(uint256) { assert(x >= y); uint256 z = x - y; return z; } function safeMult(uint256 x, uint256 y) internal returns(uint256) { uint256 z = x * y; assert((x == 0)||(z/x == y)); return z; }}contract Token { uint256 public totalSupply; function balanceOf(address _owner) constant returns (uint256 balance); function transfer(address _to, uint256 _value) returns (bool success); function transferFrom(address _from, address _to, uint256 _value) returns (bool success); function approve(address _spender, uint256 _value) returns (bool success); function allowance(address _owner, address _spender) constant returns (uint256 remaining); event Transfer(address indexed _from, address indexed _to, uint256 _value); event Approval(address indexed _owner, address indexed _spender, uint256 _value);}/* ERC 20 token */contract StandardToken is Token { function transfer(address _to, uint256 _value) returns (bool success) { if (balances[msg.sender] >= _value && _value > 0) { balances[msg.sender] -= _value; balances[_to] += _value; Transfer(msg.sender, _to, _value); return true; } else { return false; } } function transferFrom(address _from, address _to, uint256 _value) returns (bool success) { if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0) { balances[_to] += _value; balances[_from] -= _value; allowed[_from][msg.sender] -= _value; Transfer(_from, _to, _value); return true; } else { return false; } } function balanceOf(address _owner) constant returns (uint256 balance) { return balances[_owner]; } function approve(address _spender, uint256 _value) returns (bool success) { allowed[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); return true; } function allowance(address _owner, address _spender) constant returns (uint256 remaining) { return allowed[_owner][_spender]; } mapping (address => uint256) balances; mapping (address => mapping (address => uint256)) allowed;}contract BliBliToken is StandardToken, SafeMath { // metadata string public constant name = "BliBli"; string public constant symbol = "BCoin"; uint256 public constant decimals = 18; string public version = "1.0"; // contracts address public ethFundDeposit; // ETH存放地址 address public newContractAddr; // token更新地址 // crowdsale parameters bool public isFunding; // 状态切换到true uint256 public fundingStartBlock; uint256 public fundingStopBlock; uint256 public currentSupply; // 正在售卖中的tokens数量 uint256 public tokenRaised = 0; // 总的售卖数量token uint256 public tokenMigrated = 0; // 总的已经交易的 token uint256 public tokenExchangeRate = 625; // 625 BILIBILI 兑换 1 ETH // events event AllocateToken(address indexed _to, uint256 _value); // 分配的私有交易token; event IssueToken(address indexed _to, uint256 _value); // 公开发行售卖的token; event IncreaseSupply(uint256 _value); event DecreaseSupply(uint256 _value); event Migrate(address indexed _to, uint256 _value); // 转换 function formatDecimals(uint256 _value) internal returns (uint256 ) { return _value * 10 ** decimals; } // constructor function BliBliToken( address _ethFundDeposit, uint256 _currentSupply) { ethFundDeposit = _ethFundDeposit; isFunding = false; //通过控制预CrowdS ale状态 fundingStartBlock = 0; fundingStopBlock = 0; currentSupply = formatDecimals(_currentSupply); totalSupply = formatDecimals(10000000); balances[msg.sender] = totalSupply; if(currentSupply > totalSupply) throw; } modifier isOwner() { require(msg.sender == ethFundDeposit); _; } /// 设置token汇率 function setTokenExchangeRate(uint256 _tokenExchangeRate) isOwner external { if (_tokenExchangeRate == 0) throw; if (_tokenExchangeRate == tokenExchangeRate) throw; tokenExchangeRate = _tokenExchangeRate; } /// @dev 超发token处理 function increaseSupply (uint256 _value) isOwner external { uint256 value = formatDecimals(_value); if (value + currentSupply > totalSupply) throw; currentSupply = safeAdd(currentSupply, value); IncreaseSupply(value); } /// @dev 被盗token处理 function decreaseSupply (uint256 _value) isOwner external { uint256 value = formatDecimals(_value); if (value + tokenRaised > currentSupply) throw; currentSupply = safeSubtract(currentSupply, value); DecreaseSupply(value); } /// 启动区块检测 异常的处理 function startFunding (uint256 _fundingStartBlock, uint256 _fundingStopBlock) isOwner external { if (isFunding) throw; if (_fundingStartBlock >= _fundingStopBlock) throw; if (block.number >= _fundingStartBlock) throw; fundingStartBlock = _fundingStartBlock; fundingStopBlock = _fundingStopBlock; isFunding = true; } /// 关闭区块异常处理 function stopFunding() isOwner external { if (!isFunding) throw; isFunding = false; } /// 开发了一个新的合同来接收token(或者更新token) function setMigrateContract(address _newContractAddr) isOwner external { if (_newContractAddr == newContractAddr) throw; newContractAddr = _newContractAddr; } /// 设置新的所有者地址 function changeOwner(address _newFundDeposit) isOwner() external { if (_newFundDeposit == address(0x0)) throw; ethFundDeposit = _newFundDeposit; } ///转移token到新的合约 function migrate() external { if(isFunding) throw; if(newContractAddr == address(0x0)) throw; uint256 tokens = balances[msg.sender]; if (tokens == 0) throw; balances[msg.sender] = 0; tokenMigrated = safeAdd(tokenMigrated, tokens); IMigrationContract newContract = IMigrationContract(newContractAddr); if (!newContract.migrate(msg.sender, tokens)) throw; Migrate(msg.sender, tokens); // log it } /// 转账ETH 到BILIBILI团队 function transferETH() isOwner external { if (this.balance == 0) throw; if (!ethFundDeposit.send(this.balance)) throw; } /// 将BILIBILI token分配到预处理地址。 function allocateToken (address _addr, uint256 _eth) isOwner external { if (_eth == 0) throw; if (_addr == address(0x0)) throw; uint256 tokens = safeMult(formatDecimals(_eth), tokenExchangeRate); if (tokens + tokenRaised > currentSupply) throw; tokenRaised = safeAdd(tokenRaised, tokens); balances[_addr] += tokens; AllocateToken(_addr, tokens); // 记录token日志 } /// 购买token function () payable { if (!isFunding) throw; if (msg.value == 0) throw; if (block.number < fundingStartBlock) throw; if (block.number > fundingStopBlock) throw; uint256 tokens = safeMult(msg.value, tokenExchangeRate); if (tokens + tokenRaised > currentSupply) throw; tokenRaised = safeAdd(tokenRaised, tokens); balances[msg.sender] += tokens; IssueToken(msg.sender, tokens); //记录日志 }}
    有了智能合约,然后把它发布到以太坊网络中。我们使用 Remix - Solidity IDE 网站来发布智能合约。MetaMask会把当前账户相关的信息填写到网站上,我们只需要把智能合约的代码粘贴进去,简单的改一下配置就可以了:


    把网站当前使用的 solidity 编译器版本号改成和文件头一致,把Enable Optimization 去掉。然后切换到 Compile 页面,点击Start Compile。


    在Run页面,点击 Deploy


    弹出 MetaMask 确认页面,输入一个 Gas 数量即可,点击Submit。


    可以看到合约开始部署~


    部署完毕后,点击那个合约,会帮你打开一个网站,查看合约详情:


    点击合约地址,会跳转到合约校验发布页面,点击Verify And Publish


    填写好下面信息,同时粘贴代码:


    拉到最下面,然后确认即可。发布成功后,会看到如下页面:


    然后在账户中,就可以看到如下内容了:

    到此为止,ERC20的合约已经部署完成!
    2 留言 2021-06-17 09:27:51 奖励46点积分
  • 基于SSM的超市订单管理系统

    1 系统需求分析超市订单管理系统是一个专为连锁店、超市等商业场所提供订单管理平台的系统。该系统的目标是建立一个订单管理平台,为需要合理规划超市供应链、供应商以及工作人员提供的便捷的平台。该系统的主要业务需求包括记录并维护某超市的供应商信息,以及该超市与供应商之间的交易订单信息,包括三种角色,系统管理员经理,普通员工。
    1.1 系统功能分析本系统主要的功能是实现超市订单管理功能,以便为超市、连锁店提供以及其他负责人提供订单详情、联系方式等,系统的主要功能有以下五个方面:

    登录/注销:管理员可以在网站上登录浏览,离开时注销并退出
    订单管理:管理员可以浏览所有订单信息,并且通过点击查看了解订单详情信息
    供应商管理:管理员可以在网站浏览所有供应商信息,并在在与其他供应商达成合作之后,添加相关供应商信息,并且通过点击查看了解他们的联系方式等
    用户管理:管理员可以管理所有超市员工用户,对用户进行增删改查,对于离职或其他原因的未工作用户给予注销管理
    密码修改:管理员可对自己的账号密码进行修改,填写对应之前的正确密码以及新密码之后,即完成相关修改密码操作
    搜索功能:在以上管理界面中,均允许了管理员根据关键字进行搜索,要求搜索框中输入的字段必须完全包含在物品名称中,否则无法查询


    1.2 系统功能需求根据系统功能要求,该超市订单管理系统以管理员为中心的用户角色,可以将系统分解成几个模块来分别设计应用程序界面,如图 1.1所示。

    1.3 系统性能需求超市订单管理系统的开发是在Window10平台上,以SSM为架构,采用MySQL 作为数据库管理系统管理后台数据库。本系统是超市信息管理建设中必不可少的一部分,它实现了现代管理信息系统的大部分功能需要。使用本系统可以使超市管理更加方便快捷,合理的页面设计也使得这个用户充分享受到基于Internet管理信息系统的优越。本系统开发说明:
    1.3.1 功能完备在开发初期,查看了大量关于电子商务,管理信息系统,J2EE等方面的资料,同时借鉴了很多其他电子商务网站和管理信息的流程。经过总结,确定了满足需求分析的基本模块。系统总体设计上实现了整个系统模块的划分,系统主要包含5大模块,分别是:订单管理信息,供应商管理,用户管理,修改密码,登陆退出系统,基本上实现了综合管理系统的所有功能。 
    1.3.2 界面友好系统用户登陆到管理页面后,每页有导航和引领的作用。系统具有自适应的能力,同时导航条方便快捷的引导用户进行各种合理的操作。
    1.3.3 管理科学本系统一开始就从管理学的角度做出了详细细致的考虑,后来有参考了电子商务管理等,最后才做出了系统总体设计,因此可以讲该系统是较为科学的。
    系统的性能需求主要表现在数据库中的各个表需要频繁地被插入、删除以及更新。对于用户来说,系统地响应时间不宜太长,否则会降低用户体验。为此要求我们建立良好的表结构,加上足够的存储空间以及硬件性能。
    2 可行性分析2.1 研究前提随着我国经济情况的日新月异,飞速发展,涌现出许许多多的超市和便利店。越来越多的人喜欢到超市购物,超市里销售的商品也呈现出多种多样的变化趋势。我们开发一个超市订单管理系统,它可以对仓储各环节实施全过程控制管理,对整个进货、退货、盘点等各个环节的规范化作业,控制整个过程的正常运行。去掉了手工书写票据和送到机房输入的步骤,解决库房信息陈旧滞后的弊病,方便了仓库管理人员对物品的放置和调配,提高了工作效率。
    该系统容易被接受,具有简单易学性,便于管理等功能,是对超市订单管理的一种有效工具。
    2.2 设计要求2.2.1 安全性超市订单管理增强对产品规范的审计,重点确定该项目中需要审计的产品。买家只能针对卖家允许公开的信息进行查阅。买家只享受对自己账号内数据的查阅权,与定后处理权,订货支付权,申请退货权,不允许偷窥其他人。卖家只能针对买家允许公开的信息进行查阅。卖家只享受对自己账号内数据的查阅权,发货权,退款相应处理权,不允许偷窥其他人。
    2.2.2 系统性能管理员登录查看超市供应商与超市员工用户管理,可以进行增、删、改、查等操作。超市订单系统可以使超市的管理趋于正规化、现代化和系统化。本项目的产品可以达到以下目标:

    提高工作效率,减少返工
    业务流程的流水线化
    符合相关标准和规则
    与目前的应用产品相比较,提高了可用性或减少了失效程度

    2.2.3 可扩展性所有信息呈现,操作完全由打开的网页呈现并完成。本系统所占有的是超市市场,它追求的是简单、易学、易用,能够更好地解决管理人员的负担,能够辅助超市有效的管理物品。对于订单管理系统的用户,可满足对订单管理的需求,且此种需求被接受并且满足,其系统便可以推广。

    3 数据库设计3.1 数据库需求分析经过对超市管理系统的调查分析,得出用户的需求大致如下:

    管理员可以在系统中对订单、供应商以及用户进行增、删、改、查的处理
    管理员需要输入账号密码登录,并且可以增添新的管理员

    如下是利用数据流图方法对数据库做需求分析:
    第一步:由用户的需求,可以得到顶层数据流图如图3.1.1所示。

    第二步:超市订单管理系统的第1层数据流图如图3.1.2所示。

    第三步:超市订单管理系统的第2层数据库流图——订单管理的细化数据流图如图3.1.3所示。

    第四步:超市订单管理系统的第2层数据流库——供应商管理的细化数据流图如图3.1.4所示。

    第五步:超市订单管理系统的第2层数据流库——用户管理的细化数据流图如图3.1.5所示。

    根据如上的数据流程图,可以列出以下记录超市订单管理所需的数据项和数据结构:

    管理员:管理员ID、管理员姓名、管理员密码、管理员性别、管理员角色、管理员出生日期、管理员电话、管理员住址
    订单:订单编码、商品名称、供应商名称、订单金额、是否付款
    供应商:供应商编码、供应商名称、联系人、联系电话、微信

    3.2 数据库概念结构设计本系统一共有用户、供应商、订单、角色、地址这五个基本实体。
    管理员可以对应多个订单,而一个订单只能对应于一个管理员。管理员可以管理多个供应商,而一个供应商只能对应于一个管理员。一个供应商可以对应多条订单,但一条订单只能对应于一个供应商。此外,有一个用户对应一个角色,一个角色对应多个用户;一个地址对应多个订单,一个订单对应一个地址。数据库表之间的关系如下:


    用户:主键ID、用户编码、用户名称、用户密码、性别、出生日期、手机、地址、用户角色、创建者、创建时间、更新者、更新时间、用户头像、工作照
    账单:订单编号、订单编码、商品名称、商品描述、商品单位、商品数量、商品总额、是否支付、创建者、创建时间、更新者、更新时间、供应商ID
    供应商:供应商ID、供货商编码、供货商名称、供应商详细描述、供应商联系人、联系电话、地址、微信、创建者、创建时间、更新时间、更新者、营业执照、组织机构代码证
    地址:主键ID、联系人姓名、收货地址明细、邮编、联系人电话、创建者、创建日期、修改者、修改时间、用户ID
    角色:角色编号、角色编码、角色名称、创建者、创建时间、修改者、修改时间

    3.3 数据库逻辑结构设计将概念结构设计中的各个模型转化为DBMS支持的表结构,同时保持不会出现插入异常、删除异常和修改异常,表结构应该做到符合3NF。根据系统 E-R 图,需要设计4个数据表来存放信息。在本系统中,一共有五个实体,实体转化为数据库模型为如下所示:
    一对多联系转化为一个关系模式:

    用户—订单(用户编号,订单编号)
    供货商—订单(供货商编号,订单编号)
    用户—身份(用户编号,身份编号)
    用户—地址(用户编号)

    利用以上关系模式得到的所有数据表如下所示:
    用户表(smbms_user)

    数据项:主键ID、用户编码、用户名称、用户密码、性别、出生日期、手机、地址、用户角色、创建者、创建时间、更新者、更新时间、用户头像、工作照
    说明:用户ID是唯一的用户标识,使此表的主键。如表3.3.1所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    主键ID


    userCode
    varchar
    15
    Not null
    用户编码


    userName
    varchar
    15
    Not null
    用户名称


    userPassword
    varchar
    15
    Not null
    用户密码


    gender
    int
    10

    性别


    birthday
    date


    出生日期


    phone
    varchar
    15

    手机


    address
    varchar
    30

    地址


    userRole
    int
    10

    用户角色


    createdBy
    bigint
    20

    创建者


    creationDate
    datetime


    创建时间


    modifyBy
    bigint
    20

    更新者


    modifyDate
    datetime


    更新时间


    idPicPath
    varchar
    300

    用户头像


    workPicPath
    varchar
    300

    工作照



    供应商表(smbms_provider)

    数据项:供应商ID、供货商编码、供货商名称、供应商详细描述、供应商联系人、联系电话、地址、微信、创建者、创建时间、更新时间、更新者、营业执照、组织机构代码证
    说明:这张表标识的是超市管理信息系统中商品供应商的信息列表,供应商ID是该表的主键
    编号方法:商品供应商ID采用自动生成方式,如表3.3.2所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    Bigint
    20
    Not null
    供货商ID(主键)


    proCode
    Varchar
    20
    Not null
    供货商编码


    proName
    varchar
    20
    Not null
    供货商名称


    ProDesc
    varchar
    50

    供应商详细描述


    proContact
    varchar
    20
    Not null
    供货商联系人


    proPhone
    Varchar
    20
    Not null
    联系电话


    ProAddress
    Varchar
    50
    Not null
    供货商地址


    proFax
    varchar
    20

    微信


    CreateBy
    bigint
    20

    创建者


    CreatationDate
    datetime


    创建时间


    modifyDate
    datetime


    更新时间


    modifyBy
    bigint
    20

    更新者


    companyLicPicPath
    varchar
    300

    营业执照


    orgCodePicPath
    varchar
    300

    组织机构代码证



    订单表(smbms_bill)

    数据项:订单编号、订单编码、商品名称、商品描述、商品单位、商品数量、商品总额、是否支付、创建者、创建时间、更新者、更新时间、供应商ID
    说明:这张表标识的是超市管理信息系统订单信息列表,订单ID是该表的主键
    编号方法:订单ID采用自动生成方式,供应商ID与供应商表中供应商ID一一对应,如表3.3.3所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    订单ID(主键)


    billCode
    varchar
    20
    Not null
    订单编码


    ProductName
    Varchar
    20
    Not null
    商品名称


    ProductDescent
    Varchar
    50
    Not null
    商品描述


    ProductUnit
    Varchar
    10
    Not null
    商品单位


    ProductCount
    Decimal
    20,2
    Not null
    商品数量


    totalPrice
    Decimal
    20,2
    Not null
    商品总额


    isPayment
    int
    10
    Not null
    是否支付


    createdBy
    bigint
    20

    创建者


    creationDate
    Datetime


    创建时间


    modifyBy
    bigint
    20

    更新者


    modifyDate
    datetime


    更新时间


    providerID
    Int
    20

    供应商ID



    身份表(smbms_role)

    数据项:角色编号、角色编码、角色名称、创建者、创建时间、修改者、修改时间
    说明:这张表标识的是超市订单管理信息系统中用户身份列表,身份编号是该表的主键
    编号方法:用户身份编号与用户表中的员工身份编号一一对应,如表3.3.4所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    角色ID(主键)


    RoleCode
    varchar
    15
    Not null
    角色编码


    roleName
    Varchar
    15
    Not null
    角色名称


    createdBy
    bigint
    20

    创建者


    creationDate
    datetime


    创建时间


    modifyBy
    bigint
    20

    修改者


    modifyDate
    datetime


    修改时间



    地址表(smbms_address)

    数据项:主键ID、联系人姓名、收货地址明细、邮编、联系人电话、创建者、创建日期、修改者、修改时间、用户ID
    编号方法:用户ID与用户表中的用户ID一一对应,如表3.3.5所示。




    列名
    数据类型
    数据长度
    可否为空
    备注




    Id
    bigint
    20
    Not null
    主键ID(主键)


    Contact
    varchar
    15
    Not null
    联系人姓名


    addressDesce
    Varchar
    50
    Not null
    收货地址明细


    postcode
    Varchar
    15

    邮编


    Tel
    Varchar
    20
    Not null
    联系人电话


    createdBy
    bigint
    20

    创建者


    creationDate
    Datetime


    创建时间


    modifyBy
    bigint
    20

    修改者


    modifyDate
    datetime


    修改时间


    userID
    Bigint
    20

    用户ID



    数据库连接利用了SSM框架的底层的MyBatis,建立了实体类与MySQL之间映射关系,从而实现数据持久化、封装数据库连接等操作。
    3.4 数据库物理结构设计3.4.1 选择关系模式的存取方式对数据库逻辑结构设计中建立的表结构,供应商表的供应商编号属性唯一决定每一个供应商元组,所以对供应商表建立以供应商编号为主关键字的索引。同理,对管理员关系模式、订单关系模式也采用类似的索引存取方法。
    3.4.2 数据表存储结构设计本系统的所有数据表均存放在物理磁盘中。用户表、供应商表和订单表的结构是相对稳定的,表中的已有记录是要长期保存的,在此基础上系统会相应用户的操作对数据表进行增、删、改、查等操作。

    3.5 数据库的建立3.5.1 数据库的建立创建数据库
    create database smbms;USE smbms;
    创建表smbms_address
    DROP TABLE IF EXISTS `smbms_address`;CREATE TABLE `smbms_address` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `contact` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '联系人姓名', `addressDesc` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '收货地址明细', `postCode` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '邮编', `tel` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '联系人电话', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '修改者', `modifyDate` datetime DEFAULT NULL COMMENT '修改时间', `userId` bigint(20) DEFAULT NULL COMMENT '用户ID', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_bill
    DROP TABLE IF EXISTS `smbms_bill`;CREATE TABLE `smbms_bill` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `billCode` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '账单编码', `productName` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '商品名称', `productDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '商品描述', `productUnit` varchar(10) COLLATE utf8_unicode_ci NOT NULL COMMENT '商品单位', `productCount` decimal(20,2) NOT NULL COMMENT '商品数量', `totalPrice` decimal(20,2) NOT NULL COMMENT '商品总额', `isPayment` int(10) NOT NULL COMMENT '是否支付(1:未支付 2:已支付)', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)', `modifyDate` datetime DEFAULT NULL COMMENT '更新时间', `providerId` int(20) DEFAULT NULL COMMENT '供应商ID', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_provider
    DROP TABLE IF EXISTS `smbms_provider`;CREATE TABLE `smbms_provider` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `proCode` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '供应商编码', `proName` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '供应商名称', `proDesc` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '供应商详细描述', `proContact` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '供应商联系人', `proPhone` varchar(20) COLLATE utf8_unicode_ci NOT NULL COMMENT '联系电话', `proAddress` varchar(50) COLLATE utf8_unicode_ci NOT NULL COMMENT '地址', `proFax` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '微信', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyDate` datetime DEFAULT NULL COMMENT '更新时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)', `companyLicPicPath` varchar(300) DEFAULT NULL COMMENT '营业执照', `orgCodePicPath` varchar(300) DEFAULT NULL COMMENT '组织机构代码证', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_role
    DROP TABLE IF EXISTS `smbms_role`;CREATE TABLE `smbms_role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `roleCode` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '角色编码', `roleName` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '角色名称', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '修改者', `modifyDate` datetime DEFAULT NULL COMMENT '修改时间', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    创建表smbms_user
    DROP TABLE IF EXISTS `smbms_user`;CREATE TABLE `smbms_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `userCode` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '用户编码', `userName` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '用户名称', `userPassword` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '用户密码', `gender` int(10) DEFAULT 2 COMMENT '性别(1:女、 2:男)', `birthday` date DEFAULT NULL COMMENT '出生日期', `phone` varchar(15) COLLATE utf8_unicode_ci NOT NULL COMMENT '手机', `address` varchar(30) COLLATE utf8_unicode_ci NOT NULL COMMENT '地址', `userRole` int(10) DEFAULT NULL COMMENT '用户角色(取自角色表-角色id)', `createdBy` bigint(20) DEFAULT NULL COMMENT '创建者(userId)', `creationDate` datetime DEFAULT NULL COMMENT '创建时间', `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)', `modifyDate` datetime DEFAULT NULL COMMENT '更新时间', `idPicPath` varchar(300) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '用户头像', `workPicPath` varchar(300) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '工作照', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    3.5.2 初始数据的输入数据表创建成功后,数据库中还没有实际的数据。为了保证外部键能使用,数据需要提前输入,如用户编码、用户姓名、订单名称和供应商等等。具体插入语句如下:
    向smbms_address表插入数据
    insert into `smbms_address`(`id`,`contact`,`addressDesc`,`postCode`,`tel`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`,`userId`) values (1,'王丽','北京市东城区东交民巷44号','100010','13678789999',1,'2020-04-13 00:00:00',NULL,NULL,1),(2,'张红丽','北京市海淀区丹棱街3号','100000','18567672312',1,'2020-04-13 00:00:00',NULL,NULL,1),(3,'任志强','北京市东城区美术馆后街23号','100021','13387906742',1,'2020-04-13 00:00:00',NULL,NULL,1),(4,'曹颖','北京市朝阳区朝阳门南大街14号','100053','13568902323',1,'2020-04-13 00:00:00',NULL,NULL,2),(5,'李慧','北京市西城区三里河路南三巷3号','100032','18032356666',1,'2020-04-13 00:00:00',NULL,NULL,3),(6,'王国强','北京市顺义区高丽营镇金马工业区18号','100061','13787882222',1,'2020-04-13 00:00:00',NULL,NULL,3);
    向smbms_bill表插入数据
    insert into `smbms_bill`(`id`,`billCode`,`productName`,`productDesc`,`productUnit`,`productCount`,`totalPrice`,`isPayment`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`,`providerId`) values (1,'BILL2016_001','洗发水、护发素','日用品-洗发、护发','瓶','500.00','25000.00',2,1,'2020-06-14 13:02:03',NULL,NULL,13),(2,'BILL2016_002','香皂、肥皂、药皂','日用品-皂类','块','1000.00','10000.00',2,1,'2020-03-23 04:20:40',NULL,NULL,13),(3,'BILL2016_003','大豆油','食品-食用油','斤','300.00','5890.00',2,1,'2020-05-14 13:02:03',NULL,NULL,6),(4,'BILL2016_004','橄榄油','食品-进口食用油','斤','200.00','9800.00',2,1,'2020-04-10 03:12:13',NULL,NULL,7),(5,'BILL2016_005','洗洁精','日用品-厨房清洁','瓶','500.00','7000.00',2,1,'2020-05-14 13:02:03',NULL,NULL,9),(6,'BILL2016_006','美国大杏仁','食品-坚果','袋','300.00','5000.00',2,1,'2020-04-14 06:08:09',NULL,NULL,4),(7,'BILL2016_007','沐浴液、精油','日用品-沐浴类','瓶','500.00','23000.00',1,1,'2020-07-01 10:10:22',NULL,NULL,14),(8,'BILL2016_008','不锈钢盘碗','日用品-厨房用具','个','600.00','6000.00',2,1,'2020-04-14 05:12:13',NULL,NULL,14),(9,'BILL2016_009','塑料杯','日用品-杯子','个','350.00','1750.00',2,1,'2020-02-04 11:40:20',NULL,NULL,14),(10,'BILL2016_010','豆瓣酱','食品-调料','瓶','200.00','2000.00',2,1,'2020-01-29 05:07:03',NULL,NULL,8),(11,'BILL2016_011','海之蓝','饮料-国酒','瓶','50.00','10000.00',1,1,'2020-04-14 16:16:00',NULL,NULL,1),(12,'BILL2016_012','芝华士','饮料-洋酒','瓶','20.00','6000.00',1,1,'2020-06-09 17:00:00',NULL,NULL,1),(13,'BILL2016_013','长城红葡萄酒','饮料-红酒','瓶','60.00','800.00',2,1,'2020-04-14 15:23:00',NULL,NULL,1),(14,'BILL2016_014','泰国香米','食品-大米','斤','400.00','5000.00',2,1,'2020-05-09 15:20:00',NULL,NULL,3),(15,'BILL2016_015','东北大米','食品-大米','斤','600.00','4000.00',2,1,'2020-05-14 14:00:00',NULL,NULL,3),(16,'BILL2016_016','可口可乐','饮料','瓶','2000.00','6000.00',2,1,'2020-03-27 13:03:01',NULL,NULL,2),(17,'BILL2016_017','脉动','饮料','瓶','1500.00','4500.00',2,1,'2020-05-10 12:00:00',NULL,NULL,2),(18,'BILL2016_018','哇哈哈','饮料','瓶','2000.00','4000.00',2,1,'2020-06-24 15:12:03',NULL,NULL,2);
    向smbms_provider表插入数据
    insert into `smbms_provider`(`id`,`proCode`,`proName`,`proDesc`,`proContact`,`proPhone`,`proAddress`,`proFax`,`createdBy`,`creationDate`,`modifyDate`,`modifyBy`) values(1,'BJ_GYS001','北京三木堂商贸有限公司','长期合作伙伴,主营产品:茅台、五粮液、郎酒、酒鬼酒、泸州老窖、赖茅酒、法国红酒等','张国强','13566667777','北京市丰台区育芳园北路','010-58858787',1,'2020-03-21 16:52:07',NULL,NULL),(2,'HB_GYS001','石家庄帅益食品贸易有限公司','长期合作伙伴,主营产品:饮料、水饮料、植物蛋白饮料、休闲食品、果汁饮料、功能饮料等','王军','13309094212','河北省石家庄新华区','0311-67738876',1,'2020-04-13 04:20:40',NULL,NULL),(3,'GZ_GYS001','深圳市泰香米业有限公司','初次合作伙伴,主营产品:良记金轮米,龙轮香米等','郑程瀚','13402013312','广东省深圳市福田区深南大道6006华丰大厦','0755-67776212',1,'2020-03-21 16:56:07',NULL,NULL),(4,'GZ_GYS002','深圳市喜来客商贸有限公司','长期合作伙伴,主营产品:坚果炒货.果脯蜜饯.天然花茶.营养豆豆.特色美食.进口食品.海味零食.肉脯肉','林妮','18599897645','广东省深圳市福龙工业区B2栋3楼西','0755-67772341',1,'2020-03-22 16:52:07',NULL,NULL),(5,'JS_GYS001','兴化佳美调味品厂','长期合作伙伴,主营产品:天然香辛料、鸡精、复合调味料','徐国洋','13754444221','江苏省兴化市林湖工业区','0523-21299098',1,'2020-02-22 16:52:07',NULL,NULL),(6,'BJ_GYS002','北京纳福尔食用油有限公司','长期合作伙伴,主营产品:山茶油、大豆油、花生油、橄榄油等','马莺','13422235678','北京市朝阳区珠江帝景1号楼','010-588634233',1,'2020-03-21 17:52:07',NULL,NULL),(7,'BJ_GYS003','北京国粮食用油有限公司','初次合作伙伴,主营产品:花生油、大豆油、小磨油等','王驰','13344441135','北京大兴青云店开发区','010-588134111',1,'2020-04-13 00:00:00',NULL,NULL),(8,'ZJ_GYS001','慈溪市广和绿色食品厂','长期合作伙伴,主营产品:豆瓣酱、黄豆酱、甜面酱,辣椒,大蒜等农产品','薛圣丹','18099953223','浙江省宁波市慈溪周巷小安村','0574-34449090',1,'2020-01-21 06:02:07',NULL,NULL),(9,'GX_GYS001','优百商贸有限公司','长期合作伙伴,主营产品:日化产品','李立国','13323566543','广西南宁市秀厢大道42-1号','0771-98861134',1,'2020-03-21 19:52:07',NULL,NULL),(10,'JS_GYS002','南京火头军信息技术有限公司','长期合作伙伴,主营产品:不锈钢厨具等','陈女士','13098992113','江苏省南京市浦口区浦口大道1号新城总部大厦A座903室','025-86223345',1,'2020-03-25 16:52:07',NULL,NULL),(11,'GZ_GYS003','广州市白云区美星五金制品厂','长期合作伙伴,主营产品:海绵床垫、坐垫、靠垫、海绵枕头、头枕等','梁天','13562276775','广州市白云区钟落潭镇福龙路20号','020-85542231',1,'2020-01-21 06:12:17',NULL,NULL),(12,'BJ_GYS004','北京隆盛日化科技','长期合作伙伴,主营产品:日化环保清洗剂,家居洗涤专卖、洗涤用品网、墙体除霉剂、墙面霉菌清除剂等','孙欣','13689865678','北京市大兴区旧宫','010-35576786',1,'2020-01-21 12:51:11',NULL,NULL),(13,'SD_GYS001','山东豪克华光联合发展有限公司','长期合作伙伴,主营产品:洗衣皂、洗衣粉、洗衣液、洗洁精、消杀类、香皂等','吴洪转','13245468787','山东济阳济北工业区仁和街21号','0531-53362445',1,'2020-01-28 10:52:07',NULL,NULL),(14,'JS_GYS003','无锡喜源坤商行','长期合作伙伴,主营产品:日化品批销','周一清','18567674532','江苏无锡盛岸西路','0510-32274422',1,'2020-04-23 11:11:11',NULL,NULL),(15,'ZJ_GYS002','乐摆日用品厂','长期合作伙伴,主营产品:各种中、高档塑料杯,塑料乐扣水杯(密封杯)、保鲜杯(保鲜盒)、广告杯、礼品杯','王世杰','13212331567','浙江省金华市义乌市义东路','0579-34452321',1,'2020-06-22 10:01:30',NULL,NULL);
    向smbms_role表插入数据
    insert into `smbms_role`(`id`,`roleCode`,`roleName`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`) values (1,'SMBMS_ADMIN','系统管理员',1,'2020-01-01 00:00:00',NULL,NULL),(2,'SMBMS_MANAGER','经理',1,'2020-02-02 00:01:00',NULL,NULL),(3,'SMBMS_EMPLOYEE','普通员工',1,'2020-02-03 00:00:00',NULL,NULL);
    向smbms_user表插入数据
    insert into `smbms_user`(`id`,`userCode`,`userName`,`userPassword`,`gender`,`birthday`,`phone`,`address`,`userRole`,`createdBy`,`creationDate`,`modifyBy`,`modifyDate`) values (1,'admin','系统管理员','1234567',1,'1983-10-10','13688889999','山东省日照市东港区成府路207号',1,1,'2020-03-21 16:52:07',NULL,NULL),(2,'liming','李明','0000000',2,'1983-12-10','13688884457','山东省日照市东港区前门东大街9号',2,1,'2020-03-01 00:00:00',NULL,NULL),(5,'hanlubiao','韩路彪','0000000',2,'2001-06-05','18567542321','山东省日照市东港区北辰中心12号',2,1,'2020-02-11 19:52:09',NULL,NULL),(6,'zhanghua','张华','0000000',1,'1980-06-15','13544561111','山东省日照市东港区学院路61号',3,1,'2020-02-11 10:51:17',NULL,NULL),(7,'wangyang','王洋','0000000',2,'2001-12-31','13444561124','山东省青岛市三二二区西二旗辉煌国际16层',3,1,'2020-06-11 19:09:07',NULL,NULL),(8,'zhaoyan','赵燕','0000000',1,'1999-03-07','18098764545','山东省青岛市东科区回龙观小区10号楼',3,1,'2020-04-21 13:54:07',NULL,NULL),(10,'sunlei','孙磊','0000000',2,'1998-01-04','13387676765','山东省日照市朝阳区管庄新月小区12楼',3,1,'2020-05-06 10:52:07',NULL,NULL),(11,'sunxing','孙兴','0000000',2,'1997-03-12','13367890900','北京市朝阳区建国门南大街10号',3,1,'2020-01-09 16:51:17',NULL,NULL),(12,'zhangchen','张晨','0000000',1,'1986-03-28','18098765434','朝阳区管庄路口北柏林爱乐三期13号楼',3,1,'2019-06-09 05:52:37',1,'2020-04-14 14:15:36'),(13,'dengchao','邓超','0000000',2,'1981-11-04','13689674534','北京市海淀区北航家属院10号楼',3,1,'2020-07-01 08:02:47',NULL,NULL),(14,'yangguo','杨过','0000000',2,'1989-01-01','13388886623','北京市朝阳区北苑家园茉莉园20号楼',3,1,'2020-02-01 03:52:07',NULL,NULL),(15,'zhaomin','赵敏','0000000',1,'1989-12-04','18099897657','山东省临沂市昌平区天通苑3区12号楼',2,1,'2020-01-12 12:02:12',NULL,NULL);
    此外,本系统中所用到的用户性别和用户身份代码如表3.5.1至表3.5.2所示。
    用户性别代码表



    代码
    说明




    1



    2




    用户身份代码



    代码
    说明




    1
    系统管理员


    2
    经理


    3
    普通员工



    4 各功能模块的设计与实现4.1 系统开发条件4.1.1 开发语言系统使用的开发语言是Java。Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程等特点。Java可以编写桌面应用程序、Web应用程序、分布式系统应用程序等。正是因为Java语言拥有如此诸多的优秀特性,所以我们选择了它作为开发超市订单管理系统,使得整个开发、调试过程更加高效。
    4.1.2 开发框架超市订单管理系统以SSM架构作为支撑,分为表现层、业务层和持久层三层,实现后台数据更新。该架构由Spring MVC、Spring和MyBatis三个开源框架整合而成,用于开发结构合理,性能优越,代码健壮的应用程序。

    4.1.3 前端框架由于本系统是Web应用,所以使用了HTML5+CSS3+JavaScript的方式实现前端页面。实现过程中参考了Bootstrap前端开发框架。Bootstrap是Twitter退出的一个用于前端开发的开源工具包。在设计前端页面时,参考了Bootstrap的相关开源代码。
    4.1.4 集成开发环境编程所使用的集成开发环境是Eclipse,是著名的跨平台的自由集成开发环境(IDE)。Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。本次系统便选用了Eclipse作为开发平台。
    4.1.5 Web应用服务器Tomcat由Apache、Sun和其他一些公司及个人共同开发而成。由于有了Sun的参与和支持,最新的Servlet和JSP规范可以在Tomcat中得到体现。因为Tomcat技术先进、性能稳定,因而成为目前比较流行的Web应用服务器。本次系统选用的便是Tomcat作为应用服务器。
    4.1.6 数据库管理系统本系统使用的数据库管理系统是MySQL Community。MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发。在WEB应用方面,MySQL是最好的RDBMS (Relational Database Management System,关系数据库管理系统)应用软件。
    系统中的数据库以及数据库中的所有关系模式都使用MySQL进行处理。
    4.2 用户界面设计完成数据库创建和功能说明以后,我们进行下一步工作,即设计用户界面,完成了系统要求的 5 项主要功能。
    我们把超市订单管理系统的窗体分成5个主要部分,如下图所示。
    4.2.1 订单管理
    4.2.2 供应商管理
    4.2.3 用户管理
    4.2.4 修改密码
    4.2.5 登录注销
    4.3 功能模块说明5.3.1 订单信息添加、查询、修改与删除订单信息查看:为了对订单浏览信息,能够实现浏览的功能是十分必要的。管理员输入需要搜索的相应信息,点击查看按钮后系统将寻找到的数据展示到网页中。

    订单信息添加:作为超市订单管理系统,订单信息的管理是很重要的。每当采购部门增加新的订单时,订单信息就要增加。超市也可能因为其它原因增加订单信息,订单添加模块都可以做出快捷的解决方案。管理员输入相应的信息,点击提交后系统将数据保存到数据库中。

    订单信息修改:根据订单编号可以查询订单详细信息,然后修改订单的所有信息。系统从数据库中读取数据并显示到页面上,管理员修改数据后,点击修改按钮,系统将更新表中的数据。

    订单信息删除:根据订单编号可以删除该订单的信息。管理员选择需要删除订单名称并点击删除按钮,系统将从数据库中删除相应数据。
    订单信息查询:在成千上万种商品种,如果人为寻找某一个商品肯定是不可能的,只有通过商品信息查询模块才能为用户或管理人员解决这个难题。根据订单名称可以查询该订单的信息。管理员输入订单名称并点击查询按钮,系统将从数据库中查询相应的数据并显示到页面上。

    5.3.2 供应商信息添加、查询、修改与删除供应商查询界面:供应商查询界面提供了供应商的信息,可根据供应商名称的关键字进行筛选查询,并提供了添加供应商、查看供应商详细信息、修改供应商信息、删除供应商的功能。

    供应商查看详情界面:在供应商查询界面点击具体供应商操作列表的查看按钮,可以查看供应商的具体信息,包括:供货商编码、供货商名称、详细描述、联系人、联系电话、地址、微信。

    供应商修改页面:若供应商信息变动,管理员可通过供应商信息修改功能对供应商信息进行更新,更新后的数据将保存到数据库中。

    商品供应商信息删除:企业倒闭或者经营策略的改变,当它对超市商品的供应没有作用时,商品供应商厂家信息的删除是正常的。管理员输入供应商名称查询数据表中的数据并显示到页面上,点击删除后系统将表中的相应数据删除。
    供应商添加界面:与供应商达成交易后,管理员在供应商添加页面填写供应商具体信息,填写完毕点击提交,添加后的数据将保存到数据库中。

    5.3.3 用户信息添加、查询、修改与删除用户管理页面:通过输入用户名和身份查询用户。当不记得用户名的具体名字时,只输入用户名的其中一个字,会检索出所有带这个字的用户,方便管理员查询管理。点击右边链接添加用户,会连接到相关网页添加用户信息。点击操作里的查看、修改等可以进行相应的改、删、查操作。

    用户信息删除:当企业员工离职时,或者经过一段时间后,会发现用户表中一些信息时无用的,用户删除模块可以解决这样的问题。
    添加用户信息:填写用户相关信息,下面有两个按钮,可以选择重置或者提交。

    5.3.4 修改密码为了系统的安全,用户的应该只有用户个人才能修改,这不仅保证了整个公司的利益也保护了个人隐私。用户在输入相应的用户编号,填写旧密码以及新密码后,点击提交,重置密码成功。发现输入错误时,可以手动删除或者点击重置按钮,重新填写。

    修改用户密码成功后,会弹出修改用户密码成功页面,如图4.3.14所示。

    5.3.5 登录/注销输入用户名以及用户密码登录进入超市订单管理界面,可以查看管理信息。管理员可以对相关数据进行增、改、查等操作,也可以注销退出系统。
    5 实训总结5.1 所遇困难在实现本系统时遇到的困难主要体现在两个方面,一是系统的前端页面的设计,二是怎样Web与数据库实现交互。
    系统前端页面的设计困难的解决是通过参考著名的前端框架Bootstrap实现的。Bootstrap框架提供了许多精美的组建、布局,还开放了源代码供参考。在此基础上我们还加入了一些利用JavaScript代码实现的美化效果,使得前端设计更加美观。
    实体Web与数据库交互的解决得益于SSM框架的三层Spring MVC、Spring和MyBatis,能够分离处理数据库与Web层的视图,从而达到交互的目的。
    此外,在编写后端的时候,变量的大小写、系统配置也是困难重重。好在,在反复编写之后,迅速熟悉的技巧,能够让页面自由切换。系统配置更是反复在网上求证,得以解决。
    5.2 实验心得这一次合作开发超市订单管理系统,从开始选择课题的困惑到最终完成了一个我们还算满意的作品,使我学到了很多东西。从设计数据库到编写后台代码,链接数据库,在网页上显示,令人印象深刻。反复查阅资料,启动Tomcat到凌晨0点,都是藏着对这次项目的努力。其实,从一开始选择哪个题目是否用SSM框架来开发我一直也犹豫过,像国内势头正旺的ThinkPHP,易学易用,完善的中文开发文档,遇到问题或者bug可以非常容易的在中文社区得到解答。但是我最后选择了SSM框架,不仅仅因为它广泛,而是我希望能够挑战自己。经过这一个周的磨练,我最大的收获除了学到了真正可以应用的知识外,更重要的是学会了项目合作开发的经验。
    16 留言 2020-08-05 15:32:05 奖励46点积分
  • 基于JAVA的仓库管理系统

    一、项目简介:WMS在经过多家公司上线运行后,为了降低物流仓储企业的信息化成本,决定全面开源此产品。产品特点:1、适用范围:第三方物流仓储企业,自营仓储等。2、技术特点:基于JAVA的WEB后台,基于ANDROID开发的PDA系统。3、功能特点:涵盖订单管理系统(OMS),仓储管理系统(WMS),计费管理系统(BMS),现场作业系统(RF),第三方接口模块4、接口支持:已经对接:SAP ECC,SAP HANA 数据库,用友U8,百胜E3,UAS。5、对接自主研发ERP管理系统(暂未开源)6、增加进销存模块7、增加BOM二、业务介绍:1、主要功能 计费配置、仓库配置、基础配置、计费管理、基础资料、仓库管理、月台管理、进货管理、出货管理、退货管理、库内管理、盘点管理、 库存查询、PDA功能、分析报表、分析图表、域验证。2、主要流程 客户下单流程,收货流程,上架流程,移货作业、拣货流程:批量拣货,按单拣货、盘点流程、计费流程。3、硬件对接 对接自主研发基于LORA物联网技术的电子货架标签模块,满足快速退货分拣,波次拣货,生产线电子流程卡。三、安装说明:1,开发环境: 开发工具: IDEA(强烈建议用IDEA,也可以Eclipse);ANDROID STUDIO JDK1.8 Maven Mysql5.6以上(linux 注意设置大小写不敏感) 运行环境:CENTOS6.5以上或windows server 2008、tomcat7以上,JDK1.8, MYSQL5.72,按照mvn方式导入3,数据库还原: 安装完数据库执行下 GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'IDENTIFIED BY '你的密码' WITH GRANT OPTION; FLUSH PRIVILEGES; 步骤 1:还原数据库,2,修改 dbconfig.properties 3.1 sql导入方式建议 登录MYSQL服务器上用source命令还原4,IDEA:tomcat7:tun 输入用户名和密码:admin llg1235、主要技术 开发语言:JAVA。6、技术架构基础架构基于jeecg。技术架构为SpringMVC+Hibernat+Minidao(类Mybatis)+Easyui(UI库)+ Jquery + Boostrap + Ehcache + Redis + Ztree等基础架构四、修改日志:1,测试环境已恢复2,增加域名五、效果图:









    4 留言 2021-06-11 20:15:43 奖励30点积分
  • 一种基于学习的方法从3D激光点云序列数据中提取运动中的物体

    Moving Object Segmentation in 3D LiDAR Data:A Learning-based Approach Exploiting Sequential Data摘要检测和分割场景中运动目标对于构建一致性的地图、状态估计、避障和规划都是很重要的能力。本文中,作者提出一种仅依赖多线LiDAR点云分割运动目标的方法。本文的方法精准的分割出场景中移动的车辆和停止的车辆,本文的方法利用多线激光点云中的序列信息,采用从序列激光点云中得到的残差影像作为中介,通过CNN从激光点云中提取运动中的目标,并且检测帧率比LiDAR点云的帧率高。本文方法还在不同神经网络上进行测试,表现出更优的性能,并在SemanticKITTI数据集上新开了一个运动物体分割的benchmark。
    方法概述其关键思想是,将过往的N帧激光点云投影到当前帧上,并采用相同的投影方式得到深度影像,并逐像素的和当前帧的深度图像做差,得到N-1个残差影像,并和当前帧组成新的输入数据传入到神经网络,检测出运动中的对象。



    注意
    本文的方法只分割出场景中运动中的车辆只要是CNN网络就可以
    0 留言 2021-06-16 12:07:05 奖励46点积分
  • ~“WRITE-BUG校园开源社区”交流群 ~

    为方便各位小伙伴的交流学习,平台创建了一个校园开源社区微信交流群
    群内的成员均是国内各大高校计算机相关专业的大学生(清华、人大、北邮… 硕士、博士都有哦!),快来扫码加入吧 ~(PS:本群组建的目的是方便各大高校计算机专业学生对课内专业技术交流和讨论!欢迎愿意交流、学习的同学加入,“伸手党”请自觉屏蔽!)

    若人数达到上限,不能直接扫码加群,可先加客服小姐姐微信,再拉您进群哦~
    (PS:客服小姐姐微信二维码如下↓,请备注:“WRITE-BUG技术共享平台”,否则不予通过哦)
    5 留言 2021-04-23 08:30:38
  • 基于ORM的框架思想与体系

    目录
    一、Scree是为了解决什么问题?
    二、Scree为了取代数据访问层是如何思考的?做了哪些事情?
    三、现在开始使用scree
    四、Scree的高阶能力
    五、分布式环境下的scree
    六、常见问题集锦

    前言第一次接触到ORM,那是技术老大自行研发的。其实,它并不仅仅是ORM,而是基于ORM的一整套框架体系。当时的我就被震撼到了,对一套框架来说,核心并不在于代码,而在于它曾经面临什么样的问题,在于它解决问题的思维方式。随着研究和使用的深入,越发觉得它的思想在当时很超前的(这里就不多提了),同时,也发现了它的一些不足和过度设计的问题。于是,我萌生了写一套自己框架的想法。说干就干,这一干,从08年开始断断续续就好几年。迭代了很多版本,逐步完善了很多功能,也删了更多的功能。其后,项目经过几年在磁盘中的封印,又经过几年在实际生产中的应用,又经过N年文档懒癌的治疗,终于下定决心开源出来。我喜欢写代码,特别喜欢逻辑思考的过程,但一点也不喜欢写项目文档,何况还是教别人怎么使用这一堆代码的文档。
    一、Scree是为了解决什么问题?要回答这个问题,不得不提非ORM的开发模式,同时也不得不提三层架构。

    三层与N层这里不展开,需要提及的是三层中的数据访问层不一定就是一层,也可以是两层、三层甚至N层。在非ORM开发模式下,通过在业务逻辑层编写SQL语句(很多时候也以视图或存储过程的形态存在),然后调用数据访问层而达到读写数据的目的。而基于ORM底层框架(这里需要加上底层这个定义,随着技术的发展、系统规模的膨胀三层框架任何一层在横向和纵向都有了大规模的层级扩展),主旨就是为了替换掉数据访问层同时变业务逻辑层的SQL操作方式为对象化操作方式,这也是scree的初衷。
    对象化操作的好处就不用多说了,谁用谁知道。ORM的出现,将面向对象语言的系统构建过程全面的对象化了,不再留有SQL时代的缺憾。数据终于全部变成了一个个、一组组的对象,一线开发人员的精气神可以更专注于业务逻辑,更有效率的团队协作、更快速的数据迁移、更灵活的系统扩展成为可能。
    二、Scree为了取代数据访问层是如何思考的?做了哪些事情? 1、自动生成表
    既然祭出了ORM的旗帜,那么class与表、object与数据行的对应关系维护就是基本需求。先看如下一段代码:
    public enum NewsType{ Military = 0, World = 1, Society = 2, Culture = 3, Travel = 4,}public class News : SRO{ [StringDataType(IsNullable = false, Length = 50)] public string Title { get; set; } [StringDataType(IsMaxLength = true)] public string Context { get; set; } public string Author { get; set; } public NewsType Type { get; set; } public int ReadingQuantity { get; set; } static News() { TimeStampService.RegisterIdFormat<News>("xw{0:yyMMdd}{1}"); }}

    Scree会自动将继承自SRO的类生成为数据库中同名的表(不支持配置不同的表名,这不是一个技术问题,应该来说最初是有这个设计的,在再三考虑下,取消这个支持。少就是多,在至简的名义追求最大的可用才是最美的。对于很多不支持的功能,不是不能支持,而是经过慎重考虑不予支持,下同,不在赘述)
    仅支持SQL Server,如果确实有其他数据库的需求,请自行修改Scree.DataBase.SQLServer
    支持数据类型为int、bool、string、DateTime、枚举、decimal、long、byte[],分别对应数据库中的int、bit、nvarchar或text、datetime、int、decimal、bigint、image
    可用通过对属性设置Attribute来指定字段类型详细信息,详见Scree.Attributes
    string默认为nvarchar(32),decimal默认为decimal(18,4)
    系统只会自动为新class创建对应的表,如果是字段有修改,则需要人工修改数据库
    SRO基类中提供了5个默认属性Id、CreatedDate、LastAlterDate、Version、IsDeleted,也就是所有scree的数据对象都会自带这5个字段,用途后面还会详解
    2、增删改查

    对数据的基本操作,莫过于增删改查,下面演示如何通过操作对象方便的读写对应的数据。

    增加
    //如果直接new也是可以的,目前是等效的,建议统一使用CreateObject,未来可以利用CreateObject搞一些事情//News news = new News();News news = PersisterService.CreateObject<News>();news.Title = "新闻标题";news.Context = "新闻内容";PersisterService.SaveObject(news);

    查询
    News obj = PersisterService.LoadObject<News>("新闻Id");

    修改
    News obj = PersisterService.LoadObject<News>("新闻Id", LoadType.DataBaseDirect);news.Title = "新的标题";news.Context = "新的内容";PersisterService.SaveObject(news);

    删除,仅支持逻辑删除,不会做物理删除,可以理解为删除本身也就是一种修改。逻辑删除的数据,通过框架查询时会自动屏蔽掉。
    News obj = PersisterService.LoadObject<News>("新闻Id", LoadType.DataBaseDirect);news.IsDeleted = true;PersisterService.SaveObject(news);
    本质上,增删改查只有两个动作:读和写。在scree中,单个对象读使用LoadObject,写使用SaveObject。在实际业务处理中,如果Load的对象是要用于修改后Save的,LoadObject的参数应该使用LoadType.DataBaseDirect(不从缓存加载的模式)。
    3、读取一组对象
    使用LoadObjects,可精确查找、可模糊查找、可排序。
    internal static News[] GetNewsByType(NewsType type, LoadType loadType){ List<IMyDbParameter> prams = new List<IMyDbParameter>(); prams.Add(DbParameterProxy.Create("Type", SqlDbType.Int, (int)type)); News[] objs = PersisterService.LoadObjects<News>("[Type]=@Type", prams.ToArray(), loadType); return objs;}internal static News[] GetNewsByAuthor(string author, LoadType loadType){ List<IMyDbParameter> prams = new List<IMyDbParameter>(); prams.Add(DbParameterProxy.Create("Author", SqlDbType.NVarChar, "%" + author + "%")); News[] objs = PersisterService.LoadObjects<News>("[Author] like @Author order by ReadingQuantity desc", prams.ToArray(), loadType); return objs;}
    4、保存一组对象
    默认为事务性保存。
    News news = PersisterService.CreateObject<News>();news.Title = "新闻标题";news.Context = "新闻内容";string remark = "增加新闻";SystemLog systemLog = LogService.CreateSystemLog(SystemLogType.AddNews, typeof(News), news.Id, remark);PersisterService.SaveObject(new SRO[] { news, systemLog });
    5、自定义对象Id
    新创建的对象,默认Id是Guid。
    private string _id = Guid.NewGuid().ToString().Replace("-", "");
    也可以自定义具有业务意义的Id(建议Id全局唯一。注意,是全局唯一,而不是单类型唯一)。Scree提供时间戳服务,可以确保Id全局唯一。

    自定义Id需要两步,首先注册Id的格式(这种注册格式大家应该很熟悉,就是格式化字符串的写法),引用时间戳服务提供的变量。
    public class News : SRO{ static News() { TimeStampService.RegisterIdFormat<News>("xw{0:yyMMdd}{1}"); }}

    然后,给新创建的对象Id赋值(注意:Id是只读属性,只能使用SetId方法对新创建的对象赋值一次)。
    News news = PersisterService.CreateObject<News>();news.SetId(TimeStampService.GetOneId<News>());
    RegisterIdFormat以及GetOneId高级使用可以详见代码注释。
    6、对象与数据的映射原理

    先说读,前面提到读使用LoadObject
    在框架内部,通过条件自动拼接出select的sql语句,从DB拉取到数据后对对象属性进行反射,逐一赋值。

    再说写,写统一使用SaveObject
    Scree通过SRO基类的IsNew属性维护对象的新老状态。新创建的对象IsNew=true,而通过LoadObject拉取到的对象IsNew=false,框架通过判断IsNew,分别拼接出insert或update的sql。如果是一组对象同时save,则是循环前述过程。
    7、对象版本
    Scree通过SRO基类的Version属性提供对象版本维护的能力。这项能力非常重要,建议无论是否使用ORM,无论使用什么样的框架与开发模式,都应该对对象(或者说行数据)增加版本号。

    每一个新创建的对象其Version默认为0(第一次insert进入数据库中,Version维持为0)对象每一次update操作后,其Version自动+1对象update sql会自动加上当前Version的where条件,以保证不会出现数据污染
    8、CreatedDate与LastAlterDate
    CreatedDate是对象第一次创建的时间,永不再变化。LastAlterDate是对象最后一次被修改的时间,每一次update都会改变。
    9、充血模型与贫血模型

    推荐使用贫血模型。这方面的争论太多了,个人认为理论和工程是两码事,让理论的归理论,工程的归工程吧。简单、层次结构清楚、工程师易于理解和使用在工程实际中太重要了。
    三、现在开始使用scree启动scree框架,只需要一行代码,在Global.asax中
    protected void Application_Start(object sender, EventArgs e){ ServiceRoot.Init();}
    在Test文件夹中,提供了两个示例项目:

    SimpleExample,简单示例,一般小型应用(单服务器或少量服务器或单表数据量在百万内),使用基本用法即可满足需求了AdvancedExample,高级示例
    启动scree框架之前,需要有两个步骤,下面以SimpleExample为例。
    1、引用程序集

    Scree.AttributesScree.CacheScree.CommonScree.Core.IoCScree.DataBaseScree.LockScree.LogScree.PersisterScree.Syn
    2、添加配置文件

    在应用程序的根目录新建名为config的文件夹,并添加文件

    log4net.configmapping.configroot.configstorage.config
    配置文件的内容可以详见示例项目。
    示例项目中dbbak文件夹中是对应的数据库备份,初次观摩scree框架,建议直接使用备份数据库,以利于快速上手。
    四、Scree的高阶能力 1、视图支持
    虽然,我一般不建议使用视图,但某些情境下,视图确实还有积极的意义。在这里,可以通过一个窍门的方式来读取视图的数据,把视图当成一张表即可。前面说到,Scree会自动将继承自SRO的类映射成DB中同名的表。例如对于视图vwNewsForUser,可以定义类型:
    public class vwNewsForUser : SRO{}
    读取视图的数据同样可以使用到LoadObject或LoadObjects,也就是说在读上面等同于数据对象。
    vwNewsForUser obj = PersisterService.LoadObject<vwNewsForUser>("视图数据Id");
    显然,视图中必须存在SRO基类默认的5个字段。不过,这并不是问题,视图总归是从数据对象表之间关联而来,必然会有一张表是关联关系中的核心表,视图5个字段就使用该对象的即可。
    2、缓存
    框架为对象或对象数组提供本地缓存功能,只需要配置即可。

    第一步,root.config中需要配置启用缓存服务,如下:
    <Service type="Scree.Cache.ICacheService, Scree.Cache" driver="Scree.Cache.CacheService, Scree.Cache"/>

    第二步,在cache.config中配置指定类型的缓存参数,单个对象或一组对象都可以,示例如下:
    <Type name="MyApp.Models.User" second="40" isfix="true" size="300"/><Type name="Scree.Cache.ArrayCache`1[[MyApp.Models.User, MyApp.Models, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]" second="60" size="300" />
    LoadObject或LoadObjects均有LoadType的参数,默认优先读取缓存数据。框架未提供分布式缓存注入功能,如果需要使用MC或Redis等,需自行修改Scree.Cache.CacheService。
    3、BeforeSave和AfterSave

    对于单个对象,在持久化之前和之后均可以搞一些事情。
    public class SystemLog : SRO{ protected override void BeforeSave() { //可以在这里搞一些事情 } protected override void AfterSave() { //可以在这里搞一些事情 }}

    在调用PersisterService.SaveObject持久化单个(或一组对象)的之前和之后也可以搞一些事情。PersisterService提供了注入接口。
    public delegate void BeforeSave(SRO[] objs);public delegate void AfterSave(SRO[] objs);void RegisterBeforeSaveMothed(BeforeSave beforeSave);void RegisterAfterSaveMothed(AfterSave afterSave);
    4、分库

    storage.config用于配置每一个数据库的连接信息,例如:
    <StorageContext name="current"> <DataSource>127.0.0.1</DataSource> <Catalog>AdvancedExample</Catalog> <UId>dbname</UId> <Pwd>dbpassword</Pwd> <Enabled>true</Enabled> <Timeout>60</Timeout> <MinPoolSize>1</MinPoolSize> <MaxPoolSize>100</MaxPoolSize></StorageContext><StorageContext name="userdb"> <DataSource>127.0.0.1</DataSource> <Catalog>AdvancedExample-User</Catalog> <UId>dbname</UId> <Pwd>dbpassword</Pwd> <Enabled>true</Enabled> <Timeout>60</Timeout> <MinPoolSize>1</MinPoolSize> <MaxPoolSize>100</MaxPoolSize></StorageContext>

    mapping.config用于配置指定对象会对应到哪一个数据库,例如:
    <Type name="MyApp.Models.User" default="current"> <StorageContext name="UserSubById" >userdb</StorageContext> <StorageContext name="UserSubByHour" >userdb</StorageContext></Type>
    上述配置表示对象User的默认库(default)是storage.config中名为current的数据库;其存储别名(alias)为UserSubById或UserSubByHour对应的库是storage.config中名为userdb的数据库。
    备注:mapping.config未指定的类型其默认库是storage.config中名为current的数据库,尚若没有名为current的配置,则默认库就是storage.config中的第一个数据库。
    5、分表
    随着单表数据量增大,分表是通常的解法。前面说过BeforeSave可以搞一些事情,下面就是一个例子。
    public class User : SRO{ protected override void BeforeSave() { this.RegisterStorageBehavior(null); this.RegisterStorageBehavior("UserSubById", "UserById" + Id.Substring(Id.Length - 1)); this.RegisterStorageBehavior("UserSubByHour", "UserByHour" + CreatedDate.Hour.ToString()); }}
    框架提供了便捷的存储行为注册接口。
    this.RegisterStorageBehavior("UserSubById", "UserById" + Id.Substring(Id.Length - 1));
    RegisterStorageBehavior方法的第一个参数是存储别名(alias),对应的第4条分库中指定的数据库;第二个参数是在该库中分表后的表名。上述例子是通过Id最后一位做散射,从前面的Id定义可知,Id的最后一位是0-9的数字,这就意味着User对象的Id散射分表有10个,从UserById0至UserById9。
    this.RegisterStorageBehavior("UserSubByHour", "UserByHour" + CreatedDate.Hour.ToString());
    同理,User根据对象生成时间的小时数做散射,可以得到24个表,从UserByHour0至UserByHour23,使用alias为UserSubByHour,从分库配置上来看,同样存储在名为AdvancedExample-User的数据库中。
    this.RegisterStorageBehavior(null);
    alias为null且未指定表名,这样,User对象将会有一份总表数据,存储在名为AdvancedExample的数据库中。
    该示例中,User对象将产生三份完全一样的数据。以上仅为示例,大家可以根据自己的业务需要定制对象存储行为。
    备注:分表是不会自动生成的,需要人工创建。
    6、SRO对象更多技能

    CurrentAlias
    因为对象存在分库,故某一个对象可能会从不同的库中读取而来,对象的CurrentAlias属性就是代表对象来源的存储别名。

    CurrentTableName
    同理,因为对象存在分表,CurrentTableName代表对象来源的表名。

    SaveMode
    public enum SROSaveMode{ //新创建或者新Load Init = 0, //新创建的对象已经第一次持久化 Insert = 1, //已有对象的再次持久化 Update = 2, //已有对象通过SaveObject试图持久化,但是对象在业务处理过程中并没有被修改过 NoChange = 3,}
    表示对象最近一次持久化的模式,借由这个属性以及前述的AfterSave,也可以视业务的需要搞一些事情。

    GetOriginalValue
    对象在被Load出来后,经过业务逻辑处理,一部分属性会被修改,通过GetOriginalValue可以获得属性被修改前的原始值。其实,在框架内部也是通过对象的当前值与原始值的比对,生成update的sql语句的。前述的SROSaveMode.NoChange也是基于此。
    7、自定义服务
    框架提供了自定义服务的注入功能,系统启动时会自动加载。以自定义的InitService服务为例。

    InitService需要继承ServiceBase,并根据需要重写Init()和Run()方法以搞一些事情。Run()会在所有服务Init()完成之后执行。在root.config中,注入服务
    <Service type="MyApp.Services.InitService, MyApp.Services" driver="MyApp.Services.InitService, MyApp.Services"/>
    8、对象自动快照
    快照,大家应该都不陌生,是数据的一个副本,用于数据备份与恢复。我有一个大胆的想法:让系统数据可以还原到任意时刻的状态。要想做到这一点,首先必须把期初数据以及之后所有数据变更都记录下来,逆向操作就可以还原到任一时刻数据状态。当然这只是理论上的,工程实际中,计算和存储成本还是相当巨大的,甚至是不可为的。不过从小处来说,如果能把数据变更都记录下来,对于我们排查系统问题却是实实在在的武器。以往,我们会在业务逻辑处理中以各种方式记录大量的日志,但这些日志往往都是片段性的、局部的,查问题的时候总是感觉日志信息不够,何况这些日志大多是记录操作行为而不是数据本身的。
    下面简单介绍下,如何自动记录对象的快照。
    PersisterService.RegisterAfterSaveMothed(SROLogging);
    通过第7项注入服务,在启动时为Save后注册持久化后的行为。
    前面说过,RegisterAfterSaveMothed会在对象持久化之后,也就是调用SaveObject方法之后会执行,通过这个注入点,可以获得刚持久化之后获得对象引用做一些事情。
    private static void SROLogging(SRO[] objs){ if (objs == null) { return; } Thread t = new Thread(SROLoggingThreadMothed); t.Start(objs);}private static void SROLoggingThreadMothed(object o){ try { SRO[] objs = (SRO[])o; LogService.SROLogging(objs); } catch (Exception ex) { LogProxy.Error(ex, false); }}internal static void SROLogging(SRO[] objs){ try { List<SRO> list = new List<SRO>(); SROLog log; foreach (SRO obj in objs) { if (obj == null || obj.SaveMode == SROSaveMode.Init || obj.SaveMode == SROSaveMode.NoChange || !(obj is INeedFullLogging)) { continue; } log = new SROLog(); log.ObjectId = obj.Id; log.ObjectType = obj.GetType().FullName; log.ObjectJson = JsonConvert.SerializeObject(obj); log.HostName = Tools.GetHostName(); list.Add(log); } if (list.Count > 0) { PersisterService.SaveObject(list.ToArray()); } } catch (Exception ex) { LogProxy.Error(ex, false); }}
    通过上述简单的一些处理,所有继承接口INeedFullLogging的SRO对象,任一次持久化均会自动创建数据副本,存储于SROLog的表中(当然也可以分库分表,不再赘述)。由此,对象的所有版本变更均会记录在案,对于bug排查是非常有利的。
    以上只是抛砖引玉,涉及更多窍门,留待大家挖掘。
    五、分布式环境下的scree 1、同步
    同步主要是为本地缓存而生的。
    本地缓存与数据库中的数据不免的存在差异。如果全部使用的分布式缓存且同一对象只有一份缓存副本,那么是不需要同步的。一般的,既然使用了缓存,数据差异的问题就是必然的、可预见的而且在业务处理过程中也应该有所考虑和应对的。缓存某种意义上也是一把双刃剑,从性能角度,缓存自然是越多越好,越久越好。随之而来的就是数据差异会越来越大,越来越广,以至于在业务处理中不免要分神去考虑应对策略。
    以电商常见的商品库存信息为例。一般商品详情的PV会在整个系统中位列前茅,商品信息自然会做缓存,而且要尽量长。但商品的库存却一直在变化中,买家看到商品库存明明还有很多,但是一旦去下单就发现库存没了,这样体验就很不好。当然,实际中也可以单独对库存进行处理,但是价格、标题等等也同样可能会变化。自营B2C的可能还好控制一些,C2C的变化就更复杂,这就是前述的分神问题,各种对象都可能需要缓存,要考虑的就多了。
    同步机制

    对象被修改并持久化后,会将同步数据包(包含对象类型、Id、Version)异步发送给同步服务器
    应用服务器定期从同步服务器拉取同步数据包
    对象Load时,如果是从缓存加载的,则需判断同步数据包,若对象被修改过且Version比当前的缓存数据Version高,则需要再次从DB拉取
    框架提供了懒惰和非懒惰两种拉取数据的模式,默认为非懒惰模式,详见synclient.config配置

    框架默认只支持了单台同步服务器,虽然这是一问题,但并不至于致命。对于大部分系统来说,这已经足够满足需求,本着求简(或者说不干扰大多数人)的原则,框架本身就不再深入了。如果确实有必要拓展同步服务器的话,架构师在此基础上自行做一些扩展即可,并不是什么难事。
    开始使用同步服务

    创建Scree.SynServerService服务(这就是同步的服务端,可以部署单独的服务器上,也可跟应用服务部署在一起),建议设为自动启动
    客户端引用Scree.Syn.Client,并在config添加synclient.config
    客户端的root.config中添加服务注册

    <Service type="Scree.Syn.Client.ISynClientService, Scree.Syn.Client" driver="Scree.Syn.Client.SynClientService, Scree.Syn.Client"/>
    2、分布式锁
    对于分布式锁,其实我是纠结的。这是一把双刃剑,特别担心被滥用,而往往担心什么,什么就一定会发生。
    public static ILockService LockService{ get { return ServiceRoot.GetService<ILockService>(); }}ILockItem[] items = new ILockItem[] { LockService.CreateLockItem<User>("UserId_1"), LockService.CreateLockItem<User>("UserId_2"), LockService.CreateLockItem<SystemLog>("LogId_1")};string lockId;bool isLockGetted = LockService.GetLock(items, out lockId);if (isLockGetted){ try { //业务逻辑 } finally { LockService.ReleaseLock(lockId); }}
    从LockService.GetLock的参数可知,这是一个数组,也就是说你可以同时锁定多个对象。同一段业务逻辑涉及多个核心对象变更,就需要同时锁一组对象。
    前面说了,框架的持久化默认是事务性的且对象本身有Version控制,正常是不会出现脏写的。但在并发比较大的情况下,把所有数据版本控制压力转移给DB,不仅显的太晚(到真正持久化时才发现数据是脏的,虽然保证了数据安全,但已经影响到了业务操作的顺利进行),另外对DB来说也是负担,可以考虑通过分布式锁在业务层面控制。
    分布式锁使用场景就不举例了,示例项目中也没有,你真正需要的时候自然会想到,不需要的时候不知道也好。
    开始使用分布式锁服务

    创建Scree.LockServerService服务(这就是锁的服务端,可以部署单独的服务器上,也可跟应用服务部署在一起),建议设为自动启动
    客户端引用Scree.Lock.Client,并在config添加lockclient.config
    客户端的root.config中添加服务注册

    <Service type="Scree.Lock.ILockService, Scree.Lock" driver="Scree.Lock.Client.LockService, Scree.Lock.Client"/>
    六、常见问题集锦为什么没有自动创建表

    确保storage.config中数据配置是正确的
    检查mapping.config中autocreatetable属性值是否为true
    检查TableCreated.config是否已经存在该类型,此文件内容会自动生成,用于记录数据库中对应表是否已经存在。如果想重新生成表,请删除表的同时删除对应的配置项,然后重启应用即可
    检查对应类的程序集名称是否已经配置在mapping.config中的Assembly节点
    如果存在分库的,检查表是否生成到了其他库,如果是,检查表的mapping配置
    分表是不会自动创建的
    0 留言 2021-06-15 17:20:39 奖励30点积分
  • SLAM回环检测方法总结

    SLAM回环检测基于视觉的回环检测根据预训练的视觉词典,如FAB-MAP 和DBoW2,利用词袋模型(BoW)测量单词间的距离.
    缺点视觉系统对光照和视角敏感
    基于LiDAR的激光检测提取局部描述子或场景签名(全局描述子)用于场景识别.
    局部描述子代表方法
    FPFH :利用局部表面法向量计算局部描述子.Gasalt3D描述子 :概率投票方法.
    局部描述子缺点由于描述子维度较高,检索效率很低.
    全局描述子代表方法
    GLAROT :编码关键点对的几何位置到直方图中.缺点:构建关键点对很耗时.Scan Context :投影激光帧到全局描述子中.优点:速度快.缺点:精度较低.ISHOT :同时编码密度和几何信息,从而提高全局描述子的精度.缺点:加入密度信息后,描述子计算的效率变低.
    0 留言 2021-06-15 09:59:29 奖励46点积分
  • 基于JAVA开发的在线题库系统

    简介本项目是基于java开发的在线题库系统,包括

    在线访问后台运营会员中心书籍中心

    管理员账号:system 密码:123456 因为线上数据和测试数据没有做到隔离,作者已经把密码修改,可用.sql在本地运行看后台效果。
    松耦合、高可用、可靠一直是软件工程的设计目标。
    项目结构

    tamguo-common 一些基础的工具类包tamguo-modules-core 依赖tamguo-common,主要是核心业务包,包括数据处理,文件处理,邮件处理,短信处理等等。tamguo-bms 书籍系统https://book.tamguo.com,书籍系统展现层tamguo-mms会员中心https://member.tamguo.comtamguo-oms后台管理系统https://admin.tamguo.comtamguo-crawler爬虫程序,单独项目运行
    数据库脚本在tamguo db目录下
    开始(1)安装 redis
    因为官网没有windows redis的版本,我们去github上下载windows版本的redis
    redis windows 下载
    下载Redis-x64-3.2.100.zip这个包,解压,执行redis-server.exe。看到这个界面Redis就算启动成功。
    (2)安装 MYSQL
    不做描述, 把tamguo下面的db文件导入mysql注意数据库名称。(3)安装 jdk1.8
    不做描述
    至此tamguo的环境基本基本上已经安装好。
    启动 tms找到tamguo-tms下面的application.propertys。

    domain.name // 页面引用静态资源的前缀member.domain.name // 会员系统跳转域名cookie.domian.name // 关系到session,本地配置成localserver.port // 服务端口spring.datasource.url // 数据库连接地址spring.datasource.username // 数据库连接用户名redis.hostname // redis 服务地址redis.port // redis 端口file.storage.path // 上传文件存放路径
    确定propertys正确后,用springboot 方式启动应用。到这里就启动成功了,访问 http://localhost:8081/
    到现在tms已经启动成功, tms 针对的用户(包括老师,学生等等)。
    启动 bms (书籍项目)和tms一样配置好application.propertys,启动application.java 即可。
    启动 mms (会员系统)和tms一样配置好application.propertys,启动application.java 即可。
    启动 oms (后台运营系统)和tms一样配置好application.propertys,启动application.java 即可。到这里tamguo的所有子系统都已经启动成功
    0 留言 2021-06-14 10:21:06
  • 关于初等数据库的理论

    定义数据库是存放数据的仓库。它的存储空间很大,可以存放百万条、千万条、上亿条数据。但是数据库并不是随意地将数据进行存放,是有一定的规则的,否则查询的效率会很低。当今世界是一个充满着数据的互联网世界,充斥着大量的数据。即这个互联网世界就是数据世界。数据的来源有很多,比如出行记录、消费记录、浏览的网页、发送的消息等等。除了文本类型的数据,图像、音乐、声音都是数据。
    数据库是一个按数据结构来存储和管理数据的计算机软件系统。数据库的概念实际包括两层意思:

    数据库是一个实体,它是能够合理保管数据的“仓库”,用户在该“仓库”中存放要管理的事务数据,“数据”和“库”两个概念结合成为数据库
    数据库是数据管理的新方法和技术,它能更合适的组织数据、更方便的维护数据、更严密的控制数据和更有效的利用数据

    发展现状在数据库的发展历史上,数据库先后经历了层次数据库、网状数据库和关系数据库等各个阶段的发展,数据库技术在各个方面的快速的发展。特别是关系型数据库已经成为目前数据库产品中最重要的一员,80年代以来, 几乎所有的数据库厂商新出的数据库产品都支持关系型数据库,即使一些非关系数据库产品也几乎都有支持关系数据库的接口。这主要是传统的关系型数据库可以比较好的解决管理和存储关系型数据的问题。随着云计算的发展和大数据时代的到来,关系型数据库越来越无法满足需要,这主要是由于越来越多的半关系型和非关系型数据需要用数据库进行存储管理,以此同时,分布式技术等新技术的出现也对数据库的技术提出了新的要求,于是越来越多的非关系型数据库就开始出现,这类数据库与传统的关系型数据库在设计和数据结构有了很大的不同, 它们更强调数据库数据的高并发读写和存储大数据,这类数据库一般被称为NoSQL(Not only SQL)数据库。 而传统的关系型数据库在一些传统领域依然保持了强大的生命力。
    标准SQL语句虽然关系型数据库有很多,但是大多数都遵循SQL(结构化查询语言,Structured Query Language)标准。 常见的操作有查询,新增,更新,删除,求和,排序等。

    查询语句:SELECT param FROM table WHERE condition 该语句可以理解为从 table 中查询出满足 condition 条件的字段 param
    新增语句:INSERT INTO table (param1,param2,param3) VALUES (value1,value2,value3) 该语句可以理解为向table中的param1,param2,param3字段中分别插入value1,value2,value3
    更新语句:UPDATE table SET param=new_value WHERE condition 该语句可以理解为将满足condition条件的字段param更新为 new_value 值
    删除语句:DELETE FROM table WHERE condition 该语句可以理解为将满足condition条件的数据全部删除
    去重查询:SELECT DISTINCT param FROM table WHERE condition 该语句可以理解为从表table中查询出满足条件condition的字段param,但是param中重复的值只能出现一次
    排序查询:SELECT param FROM table WHERE condition ORDER BY param1该语句可以理解为从表table 中查询出满足condition条件的param,并且要按照param1升序的顺序进行排序

    总体来说, 数据库的SELECT,INSERT,UPDATE,DELETE对应了我们常用的增删改查四种操作。
    关系型数据库对于结构化数据的处理更合适,如学生成绩、地址等,这样的数据一般情况下需要使用结构化的查询,例如join,这样的情况下,关系型数据库就会比NoSQL数据库性能更优,而且精确度更高。由于结构化数据的规模不算太大,数据规模的增长通常也是可预期的,所以针对结构化数据使用关系型数据库更好。关系型数据库十分注意数据操作的事务性、一致性,如果对这方面的要求关系型数据库无疑可以很好的满足。
    非关系型数据库(NoSQL)随着近些年技术方向的不断拓展,大量的NoSql数据库如MongoDB、Redis、Memcache出于简化数据库结构、避免冗余、影响性能的表连接、摒弃复杂分布式的目的被设计。
    指的是分布式的、非关系型的、不保证遵循ACID原则的数据存储系统。NoSQL数据库技术与CAP理论、一致性哈希算法有密切关系。所谓CAP理论,简单来说就是一个分布式系统不可能满足可用性、一致性与分区容错性这三个要求,一次性满足两种要求是该系统的上限。而一致性哈希算法则指的是NoSQL数据库在应用过程中,为满足工作需求而在通常情况下产生的一种数据算法,该算法能有效解决工作方面的诸多问题但也存在弊端,即工作完成质量会随着节点的变化而产生波动,当节点过多时,相关工作结果就无法那么准确。这一问题使整个系统的工作效率受到影响,导致整个数据库系统的数据乱码与出错率大大提高,甚至会出现数据节点的内容迁移,产生错误的代码信息。但尽管如此,NoSQL数据库技术还是具有非常明显的应用优势,如数据库结构相对简单,在大数据量下的读写性能好;能满足随时存储自定义数据格式需求,非常适用于大数据处理工作。
    NoSQL数据库适合追求速度和可扩展性、业务多变的应用场景。对于非结构化数据的处理更合适,如文章、评论,这些数据如全文搜索、机器学习通常只用于模糊处理,并不需要像结构化数据一样,进行精确查询,而且这类数据的数据规模往往是海量的,数据规模的增长往往也是不可能预期的,而NoSQL数据库的扩展能力几乎也是无限的,所以NoSQL数据库可以很好的满足这一类数据的存储。NoSQL数据库利用key-value可以大量的获取大量的非结构化数据,并且数据的获取效率很高,但用它查询结构化数据效果就比较差。
    目前NoSQL数据库仍然没有一个统一的标准,它现在有四种大的分类:

    键值对存储(key-value):代表软件Redis,它的优点能够进行数据的快速查询,而缺点是需要存储数据之间的关系
    列存储:代表软件Hbase,它的优点是对数据能快速查询,数据存储的扩展性强。而缺点是数据库的功能有局限性
    文档数据库存储:代表软件MongoDB,它的优点是对数据结构要求不特别的严格。而缺点是查询性的性能不好,同时缺少一种统一查询语言
    图形数据库存储:代表软件InfoGrid,它的优点可以方便的利用图结构相关算法进行计算。而缺点是要想得到结果必须进行整个图的计算,而且遇到不适合的数据模型时,图形数据库很难使用
    0 留言 2021-06-13 10:35:12 奖励28点积分
  • 基于mmseg算法的轻量级中文分词器

    背景介绍本程序是基于mmseg算法的一个轻量级中文分词器,同时集成了关键字提取,关键短语提取,关键句子提取和文章自动摘要等功能,并且提供了一个基于Jetty的web服务器,方便各大语言直接http调用,同时提供了最新版本的lucene, solr, elasticsearch的分词接口!程序自带了一个 jcseg.properties文件用于快速配置而得到适合不同场合的分词应用,例如:最大匹配词长,是否开启中文人名识别,是否追加拼音,是否追加同义词等!
    核心功能:
    [x] 中文分词:mmseg算法 + 独创的优化算法,七种切分模式。[x] 关键字提取:基于textRank算法。[x] 关键短语提取:基于textRank算法。[x] 关键句子提取:基于textRank算法。[x] 文章自动摘要:基于BM25+textRank算法。[x] 自动词性标注:基于词库+(统计歧义去除计划),目前效果不是很理想,对词性标注结果要求较高的应用不建议使用。[x] 命名实体标注:基于词库+(统计歧义去除计划),电子邮件,网址,大陆手机号码,地名,人名,货币,datetime时间,长度,面积,距离单位等。[x] Restful api:嵌入jetty提供了一个绝对高性能的server模块,包含全部功能的http接口,标准化json输出格式,方便各种语言客户端直接调用。
    中文分词:七种切分模式:
    [x] 简易模式:FMM算法,适合速度要求场合。[x] 复杂模式:MMSEG四种过滤算法,具有较高的歧义去除,分词准确率达到了98.41%。[x] 检测模式:只返回词库中已有的词条,很适合某些应用场合。[x] 最多模式:细粒度切分,专为检索而生,除了中文处理外(不具备中文的人名,数字识别等智能功能)其他与复杂模式一致(英文,组合词等)。[x] 分隔符模式:按照给定的字符切分词条,默认是空格,特定场合的应用。[x] NLP模式:继承自复杂模式,更改了数字,单位等词条的组合方式,增加电子邮件,大陆手机号码,网址,人名,地名,货币等以及无限种自定义实体的识别与返回。[x] n-gram模式:CJK和拉丁系字符的通用n-gram切分实现。
    分词功能特性:
    [x] 支持自定义词库。在lexicon文件夹下,可以随便添加/删除/更改词库和词库内容,并且对词库进行了分类。[x] 支持词库多目录加载. 配置lexicon.path中使用’;’隔开多个词库目录.[x] 词库分为简体/繁体/简繁体混合词库: 可以专门适用于简体切分, 繁体切分, 简繁体混合切分, 并且可以利用下面提到的同义词实现,简繁体的相互检索, 程序同时提供了词库两个简单的词库管理工具来进行简繁体的转换和词库的合并。[x] 中英文同义词追加/同义词匹配 + 中文词条拼音追加.词库整合了《现代汉语词典》和cc-cedict辞典中的词条,并且依据cc-cedict词典为词条标上了拼音,依据《中华同义词词典》为词条标上了同义词(尚未完成)。更改jcseg.properties配置文档可以在分词的时候加入拼音和同义词到分词结果中。[x] 中文数字和中文分数识别,例如:”一百五十个人都来了,四十分之一的人。”中的”一百五十”和”四十分之一”。并且,程序会自动将其转换为阿拉伯数字加入到分词结果中。如:150, 1/40。[x] 支持中英混合词和英中混合词的识别(维护词库可以识别任何一种组合)。例如:B超, x射线, 卡拉ok, 奇都ktv, 哆啦a梦。[x] 支持英文的类中文切分,同样使用mmseg算法来消除歧义,例如:“openarkcompiler”会被切分成:“open ark compiler”,该功能也可以被关闭。[x] 更好的拉丁支持,电子邮件,域名,小数,分数,百分数,字母和标点组合词(例如C++, c#)的识别。[x] 自定义切分保留标点. 例如: 保留&, 就可以识别k&r这种复杂词条。[x] 复杂英文切分结果的二次切分: 可以保留原组合,同时可以避免复杂切分带来的检索命中率下降的情况,例如QQ2013会被切分成: qq2013/ qq/ 2013, chenxin619315@gmail.com会被切分成: chenxin619315@gmail.com/ chenxin/ 619315/ gmail/ com。[x] 支持阿拉伯数字/小数/中文数字基本单字单位的识别,例如2012年,1.75米,38.6℃,五折,并且 Jcseg会将其转换为“5折”加入分词结果中。[x] 智能圆角半角, 英文大小写转换。[x] 特殊字母识别:例如:Ⅰ,Ⅱ;特殊数字识别:例如:①,⑩。[x] 配对标点内容提取:例如:最好的Java书《java编程思想》,‘畅想杯黑客技术大赛’,被《,‘,“,『标点标记的内容。(1.6.8版开始支持)。[x] 智能中文人名/外文翻译人名识别。中文人名识别正确率达94%以上。(中文人名可以维护lex-lname.lex,lex-dname-1.lex,lex-dname-2.lex来提高准确率),(引入规则和词性后会达到98%以上的识别正确率)。[x] 自动中英文停止词过滤功能(需要在jcseg.properties中开启该选项,lex-stopwords.lex为停止词词库)。[x] 词库更新自动加载功能, 开启一个守护线程定时的检测词库的更新并且加载(注意需要有对应词库目录下的的lex-autoload.todo文件的写入权限)。[x] 自动词性标注(目前基于词库)。[x] 自动实体的识别,默认支持:电子邮件,网址,大陆手机号码,地名,人名,货币等;词库中可以自定义各种实体并且再切分中返回。
    快速体验:终端测试:
    cd到 根目录。ant all(或者使用maven编译)运行:java -jar jcseg-core-{version}.jar你将看到如下的终端界面在光标处输入文本开始测试(输入:seg_mode参数切换可以体验各种切分算法)
    +--------Jcseg chinese word tokenizer demo-------------------+|- @Author chenxin<chenxin619315@gmail.com> ||- :seg_mode : switch to specified tokenizer mode. ||- (:complex,:simple,:most,:detect,:delimiter,:NLP,:ngram) ||- :keywords : switch to keywords extract mode. ||- :keyphrase : switch to keyphrase extract mode. ||- :sentence : switch to sentence extract mode. ||- :summary : switch to summary extract mode. ||- :help : print this help menu. ||- :quit : to exit the program. |+------------------------------------------------------------+jcseg~tokenizer:complex>>测试样板:分词文本歧义和同义词:研究生命起源,混合词: 做B超检查身体,x射线本质是什么,今天去奇都ktv唱卡拉ok去,哆啦a梦是一个动漫中的主角,单位和全角: 2009年8月6日开始大学之旅,岳阳今天的气温为38.6℃, 也就是101.48℉, 中文数字/分数: 你分三十分之二, 小陈拿三十分之五,剩下的三十分之二十三全部是我的,那是一九九八年前的事了,四川麻辣烫很好吃,五四运动留下的五四精神。笔记本五折包邮亏本大甩卖。人名识别: 我是陈鑫,也是jcseg的作者,三国时期的诸葛亮是个天才,我们一起给刘翔加油,罗志高兴奋极了因为老吴送了他一台笔记本。外文名识别:冰岛时间7月1日,正在当地拍片的汤姆·克鲁斯通过发言人承认,他与第三任妻子凯蒂·赫尔墨斯(第一二任妻子分别为咪咪·罗杰斯、妮可·基德曼)的婚姻即将结束。配对标点: 本次『畅想杯』黑客技术大赛的得主为电信09-2BF的张三,奖励C++程序设计语言一书和【畅想网络】的『PHP教程』一套。特殊字母: 【Ⅰ】(Ⅱ),英文数字: bug report chenxin619315@gmail.com or visit http://code.google.com/p/jcseg, we all admire the hacker spirit!特殊数字: ① ⑩ ⑽ ㈩.分词结果:歧义/n 和/o 同义词/n :/w 研究/vn 琢磨/vn 研讨/vn 钻研/vn 生命/n 起源/n ,/w 混合词 :/w 做/v b超/n 检查/vn 身体/n ,/w x射线/n x光线/n 本质/n 是/a 什么/n ,/w 今天/t 去/q 奇都ktv/nz 唱/n 卡拉ok/nz 去/q ,/w 哆啦a梦/nz 是/a 一个/q 动漫/n 中/q 的/u 主角/n ,/w 单位/n 和/o 全角/nz :/w 2009年/m 8月/m 6日/m 开始/n 大学/n 之旅 ,/w 岳阳/ns 今天/t 的/u 气温/n 为/u 38.6℃/m ,/w 也就是/v 101.48℉/m ,/w 中文/n 国语/n 数字/n //w 分数/n :/w 你/r 分/h 三十分之二/m ,/w 小陈/nr 拿/nh 三十分之五/m ,/w 剩下/v 的/u 三十分之二十三/m 全部/a 是/a 我的/nt ,/w 那是/c 一九九八年/m 1998年/m 前/v 的/u 事/i 了/i ,/w 四川/ns 麻辣烫/n 很/m 好吃/v ,/w 五四运动/nz 留下/v 的/u 五四/m 54/m 精神/n 。/w 笔记本/n 五折/m 5折/m 包邮 亏本/v 大甩卖 甩卖 。/w 人名/n 识别/v :/w 我/r 是/a 陈鑫/nr ,/w 也/e 是/a jcseg/en 的/u 作者/n ,/w 三国/mq 时期/n 的/u 诸葛亮/nr 是个 天才/n ,/w 我们/r 一起/d 给/v 刘翔/nr 加油/v ,/w 罗志高/nr 兴奋/v 极了/u 因为/c 老吴/nr 送了 他/r 一台 笔记本/n 。/w 外文/n 名/j 识别/v :/w 冰岛/ns 时间/n 7月/m 1日/m ,/w 正在/u 当地/s 拍片/vi 的/u 汤姆·克鲁斯/nr 阿汤哥/nr 通过/v 发言人/n 承认/v ,/w 他/r 与/u 第三/m 任/q 妻子/n 凯蒂·赫尔墨斯/nr (/w 第一/a 二/j 任/q 妻子/n 分别为 咪咪·罗杰斯/nr 、/w 妮可·基德曼/nr )/w 的/u 婚姻/n 即将/d 结束/v 。/w 配对/v 标点/n :/w 本次/r 『/w 畅想杯/nz 』/w 黑客/n 技术/n 大赛/vn 的/u 得主/n 为/u 电信/nt 09/en -/w bf/en 2bf/en 的/u 张三/nr ,/w 奖励/vn c++/en 程序设计/gi 语言/n 一书/ns 和/o 【/w 畅想网络/nz 】/w 的/u 『/w PHP教程/nz 』/w 一套/m 。/w 特殊/a 字母/n :/w 【/w Ⅰ/nz 】/w (/w Ⅱ/m )/w ,/w 英文/n 英语/n 数字/n :/w bug/en report/en chenxin/en 619315/en gmail/en com/en chenxin619315@gmail.com/en or/en visit/en http/en :/w //w //w code/en google/en com/en code.google.com/en //w p/en //w jcseg/en ,/w we/en all/en admire/en appreciate/en like/en love/en enjoy/en the/en hacker/en spirit/en mind/en !/w 特殊/a 数字/n :/w ①/m ⑩/m ⑽/m ㈩/m ./wMaven仓库:
    jcseg-core:
    <dependency> <groupId>org.lionsoul</groupId> <artifactId>jcseg-core</artifactId> <version>2.6.2</version></dependency>

    jcseg-analyzer (lucene或者solr):
    <dependency> <groupId>org.lionsoul</groupId> <artifactId>jcseg-analyzer</artifactId> <version>2.6.2</version></dependency>

    jcseg-elasticsearch
    <dependency> <groupId>org.lionsoul</groupId> <artifactId>jcseg-elasticsearch</artifactId> <version>2.6.2</version></dependency>

    jcseg-server (独立的应用服务器)
    <dependency> <groupId>org.lionsoul</groupId> <artifactId>jcseg-server</artifactId> <version>2.6.2</version></dependency>
    lucene分词接口:
    导入jcseg-core-{version}.jar和jcseg-analyzer-{version}.jardemo代码:
    //lucene 5.x//Analyzer analyzer = new JcsegAnalyzer5X(JcsegTaskConfig.COMPLEX_MODE);//available constructor: since 1.9.8//1, JcsegAnalyzer5X(int mode)//2, JcsegAnalyzer5X(int mode, String proFile)//3, JcsegAnalyzer5X(int mode, JcsegTaskConfig config)//4, JcsegAnalyzer5X(int mode, JcsegTaskConfig config, ADictionary dic)//lucene 4.x版本//Analyzer analyzer = new JcsegAnalyzer4X(JcsegTaskConfig.COMPLEX_MODE);//lucene 6.3.0以及以上版本Analyzer analyzer = new JcsegAnalyzer(JcsegTaskConfig.COMPLEX_MODE);//available constructor: //1, JcsegAnalyzer(int mode)//2, JcsegAnalyzer(int mode, String proFile)//3, JcsegAnalyzer(int mode, JcsegTaskConfig config)//4, JcsegAnalyzer(int mode, JcsegTaskConfig config, ADictionary dic)//非必须(用于修改默认配置): 获取分词任务配置实例JcsegAnalyzer jcseg = (JcsegAnalyzer) analyzer;SegmenterConfig config = jcseg.getConfig();//追加同义词, 需要在 jcseg.properties中配置jcseg.loadsyn=1config.setAppendCJKSyn(true);//追加拼音, 需要在jcseg.properties中配置jcseg.loadpinyin=1config.setAppendCJKPinyin();//更多配置, 请查看 org.lionsoul.jcseg.SegmenterConfig
    solr分词接口:
    将jcseg-core-{version}.jar和jcseg-analyzer-{version}.jar 复制到solr 的类库目录中。在solr的scheme.xml加入如下两种配置之一:
    <!-- 复杂模式分词: --><fieldtype name="textComplex" class="solr.TextField"> <analyzer> <tokenizer class="org.lionsoul.jcseg.analyzer.JcsegTokenizerFactory" mode="complex"/> </analyzer></fieldtype><!-- 简易模式分词: --><fieldtype name="textSimple" class="solr.TextField"> <analyzer> <tokenizer class="org.lionsoul.jcseg.analyzer.JcsegTokenizerFactory" mode="simple"/> </analyzer></fieldtype><!-- 检测模式分词: --><fieldtype name="textDetect" class="solr.TextField"> <analyzer> <tokenizer class="org.lionsoul.jcseg.analyzer.JcsegTokenizerFactory" mode="detect"/> </analyzer></fieldtype><!-- 检索模式分词: --><fieldtype name="textSearch" class="solr.TextField"> <analyzer> <tokenizer class="org.lionsoul.jcseg.analyzer.JcsegTokenizerFactory" mode="most"/> </analyzer></fieldtype><!-- NLP模式分词: --><fieldtype name="textSearch" class="solr.TextField"> <analyzer> <tokenizer class="org.lionsoul.jcseg.analyzer.JcsegTokenizerFactory" mode="nlp"/> </analyzer></fieldtype><!-- 空格分隔符模式分词: --><fieldtype name="textSearch" class="solr.TextField"> <analyzer> <tokenizer class="org.lionsoul.jcseg.analyzer.JcsegTokenizerFactory" mode="delimiter"/> </analyzer></fieldtype><!-- n-gram模式分词: --><fieldtype name="textSearch" class="solr.TextField"> <analyzer> <tokenizer class="org.lionsoul.jcseg.analyzer.JcsegTokenizerFactory" mode="ngram"/> </analyzer></fieldtype>
    <b>备注:</b>

    如果使用的是solr-4.x版本,请下载v1.9.7-release tag下的源码编译得到对应的jar,然后将上述xml中的v5x改成v4x即可。如果是使用的是solr-6.3.0以下版本,JcsegTokenizerFactory包名路径为:org.lionsoul.jcseg.analyzer.v5x.JcsegTokenizerFactorytokenizer定义中可以使用jcseg.properties中定义的任何配置来自定义配置,区别就是将配置名称的”.”替换成”_”即可,开启同义词:<fieldtype name="textComplex" class="solr.TextField"> <analyzer> <tokenizer class="org.lionsoul.jcseg.analyzer.JcsegTokenizerFactory" mode="complex" jsceg_loadsyn="1"/> </analyzer></fieldtype>

    elasticsearch接口:elasticsearch.version < 2.x (Not sure)
    下载最新版本的 Jcseg源码。使用maven或者ant编译打包得到 Jcseg的系列jar包(建议使用maven,ant需要自己下载对应的依赖包)。拷贝jcseg-analyzer-{version}.jar,jcseg-core-{version}.jar,jcseg-elasticsearch-{version}.jar到{ES_HOME}/plugins/analysis-jcseg目录下(自己建立该文件夹,如果不存在)。拷贝一份jcseg.properties到{ES_HOME}/config/jcseg目录下(自己建立该文件夹,如果不存在)。配置好jcseg.properties,尤其是配置lexicon.path指向正确的词库(或者将jcseg目录下的lexicon文件夹拷贝到{ES_HOME}/plugins/jcseg目录下)。参考下载的源码中的 jcseg-elasticsearch 项目下的 config/elasticsearch.yml 配置文件,将对应的配置加到{ES_HOME}/config/elasticsearch.yml中去。配置elasticsearch.yml或者mapping来使用 Jcseg分词插件(或者在query中指定)。
    elasticsearch.version >= 2.x
    下载最新版本的 Jcseg源码。使用maven或者ant编译打包得到 Jcseg的系列jar包(建议使用maven,ant需要自己下载对应的依赖包)。拷贝jcseg-analyzer-{version}.jar,jcseg-core-{version}.jar,jcseg-elasticsearch-{version}.jar到{ES_HOME}/plugins/jcseg目录下(自己建立该文件夹,如果不存在)。拷贝一份jcseg.properties到{ES_HOME}/plugins/jcseg目录下(自己建立该文件夹,如果不存在)。拷贝一份jcseg-elasticsearch/plugin/plugin-descriptor.properties到{ES_HOME}/plugins/jcseg目录下(自己建立该文件夹,如果不存在)。配置好jcseg.properties,尤其是配置lexicon.path指向正确的词库(或者将jcseg目录下的lexicon文件夹拷贝到{ES_HOME}/plugins/jcseg目录下)。参考下载的源码中的 jcseg-elasticsearch 项目下的 config/elasticsearch.yml 配置文件,将对应的配置加到{ES_HOME}/config/elasticsearch.yml中去。配置elasticsearch.yml或者mapping来使用 Jcseg分词插件(或者在query中指定)。
    elasticsearch.version >= 5.1.1
    下载最新版本的 Jcseg源码。使用maven或者ant编译打包得到 Jcseg的系列jar包(建议使用maven,ant需要自己下载对应的依赖包)。拷贝jcseg-analyzer-{version}.jar,jcseg-core-{version}.jar,jcseg-elasticsearch-{version}.jar到{ES_HOME}/plugins/jcseg目录下(自己建立该文件夹,如果不存在)。拷贝一份jcseg.properties到{ES_HOME}/plugins/jcseg目录下(自己建立该文件夹,如果不存在)。拷贝一份jcseg-elasticsearch/plugin/plugin-descriptor.properties到{ES_HOME}/plugins/jcseg目录下(自己建立该文件夹,如果不存在)。配置好jcseg.properties,尤其是配置lexicon.path指向正确的词库(或者将jcseg目录下的lexicon文件夹拷贝到{ES_HOME}/plugins/jcseg目录下)。mapping指定来使用 Jcseg分词插件(或者在query中指定)。
    可选的analyzer名字:
    jcseg : 对应Jcseg的检索模式切分算法jcseg_complex : 对应Jcseg的复杂模式切分算法 jcseg_simple : 对应Jcseg的简易切分算法 jcseg_detect : 对应Jcseg的检测模式切分算法jcseg_most : 对应Jcseg的最多分词切分算法jcseg_nlp : 对应Jcseg的NLP模式切分算法jcseg_delimiter : 对应Jcseg的分隔符模式切分算法jcseg_ngram : 对应Jcseg的n-gram模式切分算法索引级别的自定义配置:从2.5.0以上的版本开始,你可以在elasticsearch mapping的时候使用jcseg.properties中定义的任何参数来覆盖配置,区别就是将配置名称的”.”替换为”_”即可,例如:设置加载同义词:
    "settings": { "analysis": { "analyzer": { "jcseg_complex_v3": { "type": "jcseg_complex", "jcseg_maxlen": "3", "jcseg_loadsyn": "1" } } }}
    配置测试地址:
    http://localhost:9200/_analyze?analyzer=jcseg_most&text=一百美元等于多少人民币7.x版本请使用如下方式测试:
    curl 'http://localhost:9200/_analyze?pretty=true' -H 'Content-Type:application/json' -d'{ "analyzer": "jcseg_most", "text": "一百美元等于多少人民币"}'
    打印结果如下:
    { "tokens" : [ { "token" : "一", "start_offset" : 0, "end_offset" : 1, "type" : "word", "position" : 0 }, { "token" : "一百", "start_offset" : 0, "end_offset" : 2, "type" : "word", "position" : 1 }, { "token" : "百", "start_offset" : 1, "end_offset" : 2, "type" : "word", "position" : 2 }, { "token" : "美", "start_offset" : 2, "end_offset" : 3, "type" : "word", "position" : 3 }, { "token" : "美元", "start_offset" : 2, "end_offset" : 4, "type" : "word", "position" : 4 }, { "token" : "元", "start_offset" : 3, "end_offset" : 4, "type" : "word", "position" : 5 }, { "token" : "等", "start_offset" : 4, "end_offset" : 5, "type" : "word", "position" : 6 }, { "token" : "等于", "start_offset" : 4, "end_offset" : 6, "type" : "word", "position" : 7 }, { "token" : "于", "start_offset" : 5, "end_offset" : 6, "type" : "word", "position" : 8 }, { "token" : "多", "start_offset" : 6, "end_offset" : 7, "type" : "word", "position" : 9 }, { "token" : "多少", "start_offset" : 6, "end_offset" : 8, "type" : "word", "position" : 10 }, { "token" : "少", "start_offset" : 7, "end_offset" : 8, "type" : "word", "position" : 11 }, { "token" : "人", "start_offset" : 8, "end_offset" : 9, "type" : "word", "position" : 12 }, { "token" : "人民", "start_offset" : 8, "end_offset" : 10, "type" : "word", "position" : 13 }, { "token" : "人民币", "start_offset" : 8, "end_offset" : 11, "type" : "word", "position" : 14 }, { "token" : "民", "start_offset" : 9, "end_offset" : 10, "type" : "word", "position" : 15 }, { "token" : "币", "start_offset" : 10, "end_offset" : 11, "type" : "word", "position" : 16 } ]}
    也可以直接使用集成了jcseg的elasticsearch运行包:elasticsearch-jcseg,开封就可以使用。
    分词服务器:jcseg-server模块嵌入了jetty,实现了一个绝对高性能的服务器,给jcseg的全部Api功能都加上了restful接口,并且标准化了api结果的json输出格式,各大语言直接使用http客户端调用即可。
    编译jcseg:2.3.0之前的版本:
    maven编译jcseg,得到jcseg-server-{version}.jar, maven已经将依赖的jar包一起编译进去了,如果是ant编译运行时请将依赖包载入。启动jcseg server:# 在最后传入jcseg-server.properties配置文件的路径java -jar jcseg-server-{version}.jar ./jcseg-server.properties

    2.3.0之后的版本:
    maven编译jcseg,jcseg会在jcseg-server/target/jcseg-server目录下打包一个完整的项目,目录结构如下:01, config: 配置目录,jcseg-server.properties管理服务器和词库的配置,jvm.options管理jvm的参数,例如内存分配等,默认1.5G02, lib: 全部依赖的jar包目录03, lexicon: jcseg词库目录,在此更改管理词库即可04, jcseg-server: 启动管理脚本, 仅限linux 增加-d参数可以后台启动
    启动jcseg server:```bash将jcseg-server/target/jcseg-server整个目录拷贝到安装目录,设为$JS_DIR
    cd $JS_DIR
    初次运行给jcseg-server增加+x权限同步运行./jcseg-server
    后台运行./jcseg-server -d
    ### jcseg-server.properties:懒得翻译了,默默的多念几遍就会了!jcseg server configuration file with standard json syntax{
    # jcseg server configuration"server_config": { # server port "port": 1990, # default conmunication charset "charset": "utf-8", # http idle timeout in ms "http_connection_idle_timeout": 60000, # jetty maximum thread pool size "max_thread_pool_size": 200, # thread idle timeout in ms "thread_idle_timeout": 30000, # http output buffer size "http_output_buffer_size": 32768, # request header size "http_request_header_size": 8192, # response header size "http_response_header_size": 8192},# global setting for jcseg, yet another copy of the old # configuration file jcseg.properties"jcseg_global_config": { # maximum match length. (5-7) "jcseg_maxlen": 7, # recognized the chinese name. # (true to open and false to close it) "jcseg_icnname": true, # maximum length for pair punctuation text. # set it to 0 to close this function "jcseg_pptmaxlen": 7, # maximum length for chinese last name andron. "jcseg_cnmaxlnadron": 1, # Whether to clear the stopwords. # (set true to clear stopwords and false to close it) "jcseg_clearstopword": false, # Whether to convert the chinese numeric to arabic number. # (set to true open it and false to close it) like '\u4E09\u4E07' to 30000. "jcseg_cnnumtoarabic": true, # Whether to convert the chinese fraction to arabic fraction. # @Note: for lucene,solr,elasticsearch eg.. close it. "jcseg_cnfratoarabic": false, # Whether to keep the unrecognized word. # (set true to keep unrecognized word and false to clear it) "jcseg_keepunregword": true, # Whether to start the secondary segmentation for the complex english words. "jcseg_ensencondseg": true, # min length of the secondary simple token. # (better larger than 1) "jcseg_stokenminlen": 2, #thrshold for chinese name recognize. # better not change it before you know what you are doing. "jcseg_nsthreshold": 1000000, #The punctuations that will be keep in an token. # (Not the end of the token). "jcseg_keeppunctuations": "@#%.&+"},# dictionary instance setting.# add yours here with standard json syntax"jcseg_dict": { "master": { "path": [ "{jar.dir}/lexicon" # absolute path here #"/java/JavaSE/jcseg/lexicon" ], # Whether to load the part of speech of the words "loadpos": true, # Whether to load the pinyin of the words. "loadpinyin": true, # Whether to load the synoyms words of the words. "loadsyn": true, # whether to load the entity of the words. "loadentity": true, # Whether to load the modified lexicon file auto. "autoload": true, # Poll time for auto load. (in seconds) "polltime": 300 } # add more of yours here # ,"name" : { # "path": [ # "absolute jcseg standard lexicon path 1", # "absolute jcseg standard lexicon path 2" # ... # ], # "autoload": 0, # "polltime": 300 # }},# JcsegTaskConfig instance setting.# @Note: # All the config instance here is extends from the global_setting above.# do nothing will extends all the setting from global_setting"jcseg_config": { "master": { # extends and Override the global setting "jcseg_pptmaxlen": 0, "jcseg_cnfratoarabic": true, "jcseg_keepunregword": false } # this one is for keywords,keyphrase,sentence,summary extract # @Note: do not delete this instance if u want jcseg to # offset u extractor service ,"extractor": { "jcseg_pptmaxlen": 0, "jcseg_clearstopword": true, "jcseg_cnnumtoarabic": false, "jcseg_cnfratoarabic": false, "jcseg_keepunregword": false, "jcseg_ensencondseg": false } # well, this one is for NLP only ,"nlp" : { "jcseg_ensencondseg": false, "jcseg_cnfratoarabic": true, "jcseg_cnnumtoarabic": true } # add more of yours here # ,"name": { # ... # }},# jcseg tokenizer instance setting.# Your could let the instance service for you by access:# http://jcseg_server_host:port/tokenizer/instance_name# instance_name is the name of instance you define here."jcseg_tokenizer": { "master": { # jcseg tokenizer algorithm, could be: # 1: SIMPLE_MODE # 2: COMPLEX_MODE # 3: DETECT_MODE # 4: MOST_MODE # 5: DELIMITER_MODE # 6: NLP_MODE # 7: NGRAM_MODE # see org.lionsoul.jcseg.segmenter.SegmenterConfig for more info "algorithm": 2, # dictionary instance name # choose one of your defines above in the dict scope "dict": "master", # JcsegTaskConfig instance name # choose one of your defines above in the config scope "config": "master" } # this tokenizer instance is for extractor service # do not delete it if you want jcseg to offset you extractor service ,"extractor": { "algorithm": 2, "dict": "master", "config": "extractor" } # this tokenizer instance of for NLP analysis # keep it for you NLP project ,"nlp" : { "algorithm": 6, "dict": "master", "config": "nlp" } # add more of your here # ,"name": { # ... # }}}
    ### restful api:##### 1. 关键字提取:> api地址:http://jcseg_server_host:port/extractor/keywords?text=&number=&autoFilter=true|false> api参数:<pre> text: post或者get过来的文档文本 number: 要提取的关键词个数 autoFilter: 是否自动过滤掉低分数关键字</pre>> api返回:{ //api错误代号,0正常,1参数错误, -1内部错误 “code”: 0, //api返回数据 “data”: { //关键字数组 “keywords”: [], //操作耗时 “took”: 0.001 }}
    更多配置请参考:org.lionsoul.jcseg.server.controller.KeywordsController##### 2. 关键短语提取:> api地址:http://jcseg_server_host:port/extractor/keyphrase?text=&number=> api参数:<pre> text: post或者get过来的文档文本 number: 要提取的关键短语个数</pre>> api返回:{ “code”: 0, “data”: { “took”: 0.0277, //关键短语数组 “keyphrase”: [] }}
    更多配置请参考:org.lionsoul.jcseg.server.controller.KeyphraseController##### 3. 关键句子提取:> api地址:http://jcseg_server_host:port/extractor/sentence?text=&number=> api参数:<pre> text: post或者get过来的文档文本 number: 要提取的关键句子个数</pre>> api返回:{ “code”: 0, “data”: { “took”: 0.0277, //关键句子数组 “sentence”: [] }}
    更多配置请参考:org.lionsoul.jcseg.server.controller.SentenceController##### 4. 文章摘要提取:> api地址:http://jcseg_server_host:port/extractor/summary?text=&length=> api参数:<pre> text: post或者get过来的文档文本 length: 要提取的摘要的长度</pre>> api返回:{ “code”: 0, “data”: { “took”: 0.0277, //文章摘要 “summary”: “” }}
    更多配置请参考:org.lionsoul.jcseg.server.controller.SummaryController##### 5. 文章自动分词:> api地址:http://jcseg_server_host:port/tokenizer/tokenizer_instance?text=&ret_pinyin=&ret_pos=...> api参数:<pre> tokenizer_instance: 表示在jcseg-server.properties中定义的分词实例名称 text: post或者get过来的文章文本 ret_pinyin: 是否在分词结果中返回词条拼音(2.0.1版本后已经取消) ret_pos: 是否在分词结果中返回词条词性(2.0.1版本后已经取消)</pre>> api返回:{ “code”: 0, “data”: { “took”: 0.00885, //词条对象数组 “list”: [ { word: “哆啦a梦”, //词条内容 position: 0, //词条在原文中的索引位置 length: 4, //词条的词个数(非字节数) pinyin: “duo la a meng”, //词条的拼音 pos: “nz”, //词条的词性标注 entity: null //词条的实体标注 } ] }}
    更多配置请参考:org.lionsoul.jcseg.server.controller.TokenizerController# 二次开发:### 1. 中文分词Api: Javadoc参考:[Jcseg Javadoc](https://apidoc.gitee.com/lionsoul/jcseg/)##### (1). 创建JcsegTaskConfig配置对象:jcseg.properties查找步骤:* 1,寻找jcseg-core-{version}.jar目录下的jcseg.properties* 2,如果没找到继续寻找classpath下的jcseg.properties(默认已经打包了)* 3,如果没找到继续寻找user home下的jcseg.properties(除非把classpath下的jcseg.properties删除了,要不然不会到这)所以,默认情况下可以在jcseg-core-{version}.jar同目录下来放一份jcseg.properties来自定义配置。JcsegTaskConfig构造方法如下:```javaSegmenterConfig(); //不做任何配置文件查找来初始化SegmenterConfig(boolean autoLoad); //autoLoad=true会自动查找配置来初始化SegmenterConfig(java.lang.String proFile); //从指定的配置文件中初始化配置对象SegmenterConfig(InputStream is); //从指定的输入流中初始化配置对象demo代码:
    //创建SegmenterConfig使用默认配置,不做任何配置文件查找SegmenterConfig config = new SegmenterConfig();//该方法会自动按照上述“jcseg.properties查找步骤”来寻找jcseg.properties并且初始化:SegmenterConfig config = new SegmenterConfig(true);//依据给定的jcseg.properties文件创建并且初始化JcsegTaskConfigSegmenterConfig config = new SegmenterConfig("absolute or relative jcseg.properties path");//调用SegmenterConfig#load(String proFile)方法来从指定配置文件中初始化配置选项config.load("absolute or relative jcseg.properties path");
    (2). 创建ADictionary词库对象:ADictionary构造方法如下:
    ADictionary(SegmenterConfig config, java.lang.Boolean sync)//config:上述的JcsegTaskConfig实例//sync: 是否创建线程安全词库,如果你需要在运行时操作词库对象则指定true,// 如果jcseg.properties中autoload=1则会自动创建同步词库
    demo代码:
    //Jcseg提供org.lionsoul.jcseg.dic.DictionaryFactory来方便词库的创建与往后的兼容//通常可以通过// DictionaryFactory#createDefaultDictionary(SegmenterConfig)// DictionaryFactory.createSingletonDictionary(SegmenterConfig)//两方法来创建词库对象并且加载词库文件,建议使用createSingletonDictionary来创建单例词库//config为上面创建的SegmenterConfig对象.//如果给定的SegmenterConfig里面的词库路径信息正确//ADictionary会依据config里面的词库信息加载全部有效的词库;//并且该方法会依据config.isAutoload()来决定词库的同步性还是非同步性,//config.isAutoload()为true就创建同步词库, 反之就创建非同步词库,//config.isAutoload()对应jcseg.properties中的lexicon.autoload;//如果config.getLexiconPath() = null,DictionaryFactory会自动加载classpath下的词库//如果不想让其自动加载lexicon下的词库//可以调用:DictionaryFactory.createSingletonDictionary(config, false)创建ADictionary即可;ADictionary dic = DictionaryFactory.createSingletonDictionary(config);//创建一个非同步的按照config.lexPath配置加载词库的ADictioanry.ADictionary dic = DictionaryFactory.createDefaultDictionary(config, false);//创建一个同步的按照config.lexPath加载词库的ADictioanry.ADictionary dic = DictionaryFactory.createDefaultDictionary(config, true);//依据 config.isAutoload()来决定同步性,默认按照config.lexPath来加载词库的ADictionaryADictionary dic = DictionaryFactory.createDefaultDictionary(config, config.isAutoload());//指定ADictionary加载给定目录下的所有词库文件的词条.//config.getLexiconPath为词库文件存放有效目录数组.for ( String path : config.getLexiconPath() ) { dic.loadDirectory(path);}//指定ADictionary加载给定词库文件的词条.dic.load("/java/lex-main.lex");dic.load(new File("/java/lex-main.lex"));//指定ADictionary加载给定输入流的词条dic.load(new FileInputStream("/java/lex-main.lex"));//阅读下面的“如果自定义使用词库”来获取更多信息
    (3). 创建ISegment分词实例:ISegment接口核心分词方法:
    public IWord next();//返回下一个切分的词条
    demo代码:
    //依据给定的ADictionary和SegmenterConfig来创建ISegment//1, 通过ISegment.XX_MODE参数//ISegment.COMPLEX_MODE表示创建ComplexSeg复杂ISegment分词对象//ISegment.SIMPLE_MODE表示创建SimpleSeg简易Isegmengt分词对象.//ISegment.DETECT_MODE表示创建DetectSeg Isegmengt分词对象.//ISegment.SEARCH_MODE表示创建SearchSeg Isegmengt分词对象.//ISegment.DELIMITER_MODE表示创建DelimiterSeg Isegmengt分词对象.//ISegment.NLP_MODE表示创建NLPSeg Isegmengt分词对象.//ISegment.NGRAM_MODE表示创建NGramSeg Isegmengt分词对象.ISegment seg = ISegment.Type.fromIndex(mode).factory.create(config, dic);//2, 通过调用直接的模式函数// ISegment.COMPLEX为指向ComplexSeg的构造器函数接口// ISegment.SIMPLE为指向ComplexSeg的构造器函数接口// ISegment.DETECT为指向ComplexSeg的构造器函数接口// ISegment.MOST为指向ComplexSeg的构造器函数接口// ISegment.DELIMITER为指向ComplexSeg的构造器函数接口// ISegment.NLP为指向ComplexSeg的构造器函数接口// ISegment.NGRAM为指向ComplexSeg的构造器函数接口ISegment seg = ISegment.COMPLEX.factory.create(config, dic);//设置要分词的内容String str = "研究生命起源。";seg.reset(new StringReader(str));//获取分词结果IWord word = null;while ( (word = seg.next()) != null ) { System.out.println(word.getValue());}
    (4). 一个完整的例子://创建SegmenterConfig分词配置实例,自动查找加载jcseg.properties配置项来初始化SegmenterConfig config = new SegmenterConfig(true);//创建默认单例词库实现,并且按照config配置加载词库ADictionary dic = DictionaryFactory.createSingletonDictionary(config);//依据给定的ADictionary和SegmenterConfig来创建ISegment//为了Api往后兼容,建议使用SegmentFactory来创建ISegment对象ISegment seg = ISegment.COMPLEX.factory.create(config, dic);//备注:以下代码可以反复调用,seg为非线程安全//设置要被分词的文本String str = "研究生命起源。";seg.reset(new StringReader(str));//获取分词结果IWord word = null;while ( (word = seg.next()) != null ) { System.out.println(word.getValue());}
    (5). 如何自定义使用词库:从1.9.9版本开始,Jcseg已经默认将jcseg.properties和lexicon全部词库打包进了jcseg-core-{version}.jar中,如果是通过SegmenterConfig(true)构造的SegmenterConfig或者调用了SegmenterConfig#autoLoad()方法,在找不到自定义配置文件情况下Jcseg会自动的加载classpath中的配置文件,如果config.getLexiconPath() = null DictionaryFactory默认会自动加载classpath下的词库。

    1),通过SegmenterConfig设置词库路径:
    //1, 默认构造SegmenterConfig,不做任何配置文件寻找来初始化SegmenterConfig config = new SegmenterConfig();//2, 设置自定义词库路径集合config.setLexiconPath(new String[]{ "relative or absolute lexicon path1", "relative or absolute lexicon path2" //add more here});//3, 通过config构造词库并且DictionaryFactory会按照上述设置的词库路径自动加载全部词库ADictionary dic = DictionaryFactory.createSingletonDictionary(config);

    2),通过ADictionary手动加载词库:
    //1, 构造默认的SegmenterConfig,不做任何配置文件寻找来初始化SegmenterConfig config = new SegmenterConfig();//2, 构造ADictionary词库对象//注意第二个参数为false,阻止DictionaryFactory自动检测config.getLexiconPath()来加载词库ADictionary dic = DictionaryFactory.createSingletonDictionary(config, false);//3, 手动加载词库dic.load(new File("absolute or relative lexicon file path")); //加载指定词库文件下全部词条dic.load("absolute or relative lexicon file path"); //加载指定词库文件下全部词条dic.load(new FileInputStream("absolute or relative lexicon file path")); //加载指定InputStream输入流下的全部词条dic.loadDirectory("absolute or relative lexicon directory"); //加载指定目录下的全部词库文件的全部词条dic.loadClassPath(); //加载classpath路径下的全部词库文件的全部词条(默认路径/lexicon)
    2.关键字提取Api:
    1),TextRankKeywordsExtractor构造方法:
    TextRankKeywordsExtractor(ISegment seg);//seg: Jcseg ISegment分词对象

    2),demo代码:
    //1, 创建Jcseg ISegment分词对象SegmenterConfig config = new SegmenterConfig(true);config.setClearStopwords(true); //设置过滤停止词config.setAppendCJKSyn(false); //设置关闭同义词追加config.setKeepUnregWords(false); //设置去除不识别的词条ADictionary dic = DictionaryFactory.createSingletonDictionary(config);ISegment seg = ISegment.COMPLEX.factory.create(config, dic);//2, 构建TextRankKeywordsExtractor关键字提取器TextRankKeywordsExtractor extractor = new TextRankKeywordsExtractor(seg);extractor.setMaxIterateNum(100); //设置pagerank算法最大迭代次数,非必须,使用默认即可extractor.setWindowSize(5); //设置textRank计算窗口大小,非必须,使用默认即可extractor.setKeywordsNum(10); //设置最大返回的关键词个数,默认为10//3, 从一个输入reader输入流中获取关键字String str = "现有的分词算法可分为三大类:基于字符串匹配的分词方法、基于理解的分词方法和基于统计的分词方法。按照是否与词性标注过程相结合,又可以分为单纯分词方法和分词与标注相结合的一体化方法。";List<String> keywords = extractor.getKeywords(new StringReader(str));//4, output://"分词","方法","分为","标注","相结合","字符串","匹配","过程","大类","单纯"

    3),测试源码参考:org.lionsoul.jcseg.test.KeywordsExtractorTest源码
    3. Jcseg自动摘要/关键句子提取Api:
    1),TextRankSummaryExtractor构造方法:
    TextRankSummaryExtractor(ISegment seg, SentenceSeg sentenceSeg);//seg: Jcseg ISegment分词对象//sentenceSeg: Jcseg SentenceSeg句子切分对象

    2),demo代码:
    //1, 创建Jcseg ISegment分词对象SegmenterConfig config = new SegmenterConfig(true);config.setClearStopwords(true); //设置过滤停止词config.setAppendCJKSyn(false); //设置关闭同义词追加config.setKeepUnregWords(false); //设置去除不识别的词条ADictionary dic = DictionaryFactory.createSingletonDictionary(config);ISegment seg = ISegment.COMPLEX.factory.create(config, dic);//2, 构造TextRankSummaryExtractor自动摘要提取对象SummaryExtractor extractor = new TextRankSummaryExtractor(seg, new SentenceSeg());//3, 从一个Reader输入流中获取length长度的摘要String str = "Jcseg是基于mmseg算法的一个轻量级开源中文分词器,同时集成了关键字提取,关键短语提取,关键句子提取和文章自动摘要等功能,并且提供了最新版本的lucene,%20solr,%20elasticsearch的分词接口。Jcseg自带了一个%20jcseg.properties文件用于快速配置而得到适合不同场合的分词应用。例如:最大匹配词长,是否开启中文人名识别,是否追加拼音,是否追加同义词等!";String summary = extractor.getSummary(new StringReader(str), 64);//4, output://Jcseg是基于mmseg算法的一个轻量级开源中文分词器,同时集成了关键字提取,关键短语提取,关键句子提取和文章自动摘要等功能,并且提供了最新版本的lucene, solr, elasticsearch的分词接口。//-----------------------------------------------------------------//5, 从一个Reader输入流中提取n个关键句子String str = "you source string here";extractor.setSentenceNum(6); //设置返回的关键句子个数List<String> keySentences = extractor.getKeySentence(new StringReader(str));

    3),测试源码参考:org.lionsoul.jcseg.test.SummaryExtractorTest源码
    4.关键短语提取Api:
    1),TextRankKeyphraseExtractor构造方法:
    TextRankKeyphraseExtractor(ISegment seg);//seg: Jcseg ISegment分词对象

    2),demo代码:
    //1, 创建Jcseg ISegment分词对象SegmenterConfig config = new SegmenterConfig(true);config.setClearStopwords(false); //设置不过滤停止词config.setAppendCJKSyn(false); //设置关闭同义词追加config.setKeepUnregWords(false); //设置去除不识别的词条config.setEnSecondSeg(false); //关闭英文自动二次切分ADictionary dic = DictionaryFactory.createSingletonDictionary(config);ISegment seg = ISegment.COMPLEX.factory.create(config, dic);//2, 构建TextRankKeyphraseExtractor关键短语提取器TextRankKeyphraseExtractor extractor = new TextRankKeyphraseExtractor(seg);extractor.setMaxIterateNum(100); //设置pagerank算法最大迭代词库,非必须,使用默认即可extractor.setWindowSize(5); //设置textRank窗口大小,非必须,使用默认即可extractor.setKeywordsNum(15); //设置最大返回的关键词个数,默认为10extractor.setMaxWordsNum(4); //设置最大短语词长,默认为5//3, 从一个输入reader输入流中获取短语String str = "支持向量机广泛应用于文本挖掘,例如,基于支持向量机的文本自动分类技术研究一文中很详细的介绍支持向量机的算法细节,文本自动分类是文本挖掘技术中的一种!";List<String> keyphrases = extractor.getKeyphrase(new StringReader(str));//4, output://支持向量机, 自动分类

    3),测试源码参考:org.lionsoul.jcseg.test.KeyphraseExtractorTest源码
    相关附录1,词性对照:名词n、时间词t、处所词s、方位词f、数词m、量词q、区别词b、代词r、动词v、形容词a、状态词z、副词d、介词p、连词c、助词u、语气词y、叹词e、拟声词o、成语i、习惯用语l、简称j、前接成分h、后接成分k、语素g、非语素字x、标点符号w)外,从语料库应用的角度,增加了专有名词(人名nr、地名ns、机构名称nt、其他专有名词nz)。
    2,同义词管理:
    01),统一的词库分类:从2.2.0版本开始jcseg将同义词统一成了一个单独的类别-CJK_SYN,你可以将你的同义词定义直接追加到现有的同义词词库vendors/lexicons/lex-synonyms.lex中,也可以新建一个独立的词库,然后在首行增加CJK_SYN定义,将该词库归类为同义词词库,然后按照下面介绍的格式逐行或者分行增加同义词的定义。
    02),统一的同义词格式:```格式:词根,同义词1[/可选拼音],同义词2[/可选拼音],…,同义词n[/可选拼音]

    例如:单行定义:研究,研讨,钻研,研磨/yan mo,研发
    多行定义:(只要词根一样,定义的全部同义词就都属于同一个集合)中央一台,央视一台,中央第一台中央一台,中央第一频道,央视第一台,央视第一频道
    * 03),格式和要求说明:1,第一个词为同义词的根词条,这个词条必须是CJK_WORD词库中必须存在的词条,如果不存在,这条同义词定义会被忽略。2,根词会作为不同行同义词集合的区别,如果两行同义词定义的根词一样,会自动合并成一个同义词集合。3,jcseg中使用org.lionsoul.jcseg.SynonymsEntry来管理同义词集合,每个IWord词条对象都会有一个SynonymsEntry属性来指向自己的同义词集合。4,SynonymsEntry.rootWord存储了同义词集合的根词,同义词的合并建议统一替换成根词。5,除去根词外的其他同义词,jcseg会自动检测并且创建相关的IWord词条对象并且将其加入CJK_WORD词库中,也就是说其他同义词不一定要是CJK_WORD词库中存在的词条。6,其他同义词会自动继承词根的词性和实体定义,也会继承CJK_WORD词库中该词条的拼音定义(如果存在该词),也可以在词条后面通过增加”/拼音”来单独定义拼音。7,同一同义词定义的集合中的全部IWord词条都指向同一个SynonymsEntry对象,也就是同义词之间会自动相互引用。```
    3,参考文献:
    1,MMSEG算法原著:http://technology.chtsai.org/mmseg/
    4,技术交流分享
    1,使用案例典范:原语智能~语义理解,Gitee搜索~信息检索,Kooder~开源代码搜索2,NLP交流分享:微信:lionsoul2014(请备注Jcseg),QQ:1187582057(很少关注)

    This is the end line and thanks for reading !!!
    0 留言 2021-06-11 20:15:17 奖励30点积分
  • 基于layui的树表格-treeGrid

    基于layui的树表格-treeGrid1、插件js地址:tree_table_treegrid_based_on_layui/testTreeGrid/web/design/extend/treeGrid.js
    2、例子地址: 1、树状表格:tree_table_treegrid_based_on_layui/testTreeGrid/web/index.html 2、普通表格:tree_table_treegrid_based_on_layui/testTreeGrid/web/index_list.html3、快速开始详情参见demo

    这里是列表文本添加css样式
    这里输入代码
    引入treeGrid模块
    layui.config({ base: 'design/extend/' }).extend({ treeGrid:'treeGrid' }).use(['jquery','treeGrid','layer'], function(){ // To do ... });
    4、表格参数


    参数名
    类型
    是否必填写
    说明




    limit
    number

    【默认10】 ,即使不分页


    id
    string

    表格的id,很重要,比如addRow方法需要配置此参数才生效


    elem
    string

    表格的选择器


    url
    string

    获取数据的url地址


    data
    JSON

    与url的设置,二选一


    cellMinWidth
    number

    最小列宽


    idField
    string

    id字段


    treeId
    string

    树形id字段名称


    treeUpId
    string

    树形父id字段名称


    treeShowName
    string

    以树形式显示的字段


    heightRemove
    array

    【默认[]】不计算的高度,表格设定的是固定高度,此项不生效


    height
    string

    【默认window高度的100%】,支持数值和百分比,以及full-差值


    iconOpen
    boolean

    是否显示图标【默认true】


    isOpenDefault
    boolean

    节点默认是展开还是折叠【默认true】


    loading
    boolean

    是否开启加载数据的动画【默认true】


    method
    string

    获取数据的方式,GET或者POST,【默认POST】


    isPage
    boolean

    是否开启分页,【默认false】



    5、数据格式注:以下字段皆为可选。与树形结构相关的字段,比如id、parentId、name可在表格参数中配置。



    字段明
    说明




    lay_is_checked
    选中状态(true,false)


    lay_is_radio
    单选状态(true,false)


    lay_is_open
    是否展开节点


    lay_is_show
    是否显示节点


    children
    下级,array


    lay_is_row_check
    行选中


    lay_che_disabled
    禁止多选(true,false)


    lay_rad_disabled
    禁止单选(true,false)


    lay_icon_open
    打开的图标


    lay_icon_close
    关闭的图标


    lay_icon
    叶子节点图标



    方法



    方法名
    参数
    描述




    getDataMap
    tableId
    获取列表数据


    addRow
    tableId, index, data
    添加一行数据


    delRow
    tableId, data
    删除一行数据


    treeNodeOpen
    tableId, o, isOpen



    6、 效果图:
    折叠记忆

    新增节点-

    删除节点

    删除多个节点

    节点初始化隐藏、显示控制1
    {“id”:”111”, “pId”:”1”, “name”:”苹果”,”lay_is_open”:false}
    节点初始化隐藏、显示控制2

    多选操作(选中父节点,子节点被选中;全部子节点被选中,父节点也被选中;自己看效果)
    1 留言 2021-06-09 22:54:19
  • python爬取数据时遭遇动态滚动条

    from selenium import webdriverfrom selenium.webdriver.chrome.options import Optionsimport time# 创建chrome浏览器驱动,无头模式chrome_options = Options()# chrome_options.add_argument('--headless')chrome_options.add_argument("--start-maximized");driver = webdriver.Chrome("D://googleDever//chromedriver.exe",chrome_options=chrome_options)# 加载界面driver.get("https://wenku.baidu.com/search?lm=0&od=0&ie=utf-8&word=csdn")time.sleep(3)# 获取页面初始高度js = "return action=document.body.scrollHeight"height = driver.execute_script(js)# 将滚动条调整至页面底部driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')time.sleep(5)#定义初始时间戳(秒)t1 = int(time.time())#定义循环标识,用于终止while循环status = True# 重试次数num=0while status: # 获取当前时间戳(秒) t2 = int(time.time()) # 判断时间初始时间戳和当前时间戳相差是否大于30秒,小于30秒则下拉滚动条 if t2-t1 < 30: new_height = driver.execute_script(js) if new_height > height : time.sleep(1) driver.execute_script('window.scrollTo(0, document.body.scrollHeight)') # 重置初始页面高度 height = new_height # 重置初始时间戳,重新计时 t1 = int(time.time()) elif num < 3: # 当超过30秒页面高度仍然没有更新时,进入重试逻辑,重试3次,每次等待30秒 time.sleep(3) num = num+1 else: # 超时并超过重试次数,程序结束跳出循环,并认为页面已经加载完毕! print("滚动条已经处于页面最下方!") status = False # 滚动条调整至页面顶部 driver.execute_script('window.scrollTo(0, 0)') break# 打印页面源码content = driver.page_sourceprint(content)
    0 留言 2021-06-10 10:05:31 奖励30点积分
  • python爬取双色球数据更新及数据库使用


    解析网站并获取数据
    # 彩票数据所在的urlurl = 'http://datachart.500.com/ssq/'# 提取数据response = requests.get(url, headers={"User-Agent": UserAgent().chrome})# 通过xpath去解析e = etree.HTML(response.text)date_times = e.xpath('//tbody[@id="tdata"]/tr/td[1]/text()')trs = e.xpath('//tbody[@id="tdata"]/tr[not(@class)]')
    链接数据库
    # 链接数据库client = pymysql.connect(host='localhost', port=3306, user='root', password='123456', charset='utf8', db='bangumi')cursor = client.cursor()
    普通获取数据
    # 插入数据的sqlsql = 'insert into doubleballs values(0,%s,%s,%s)'for data_time, tr in zip(date_times, trs): red_ball = '-'.join(tr.xpath('./td[@class="chartBall01"]/text()')) blue_ball = tr.xpath('./td[@class="chartBall02"]/text()')[0] print("第" + data_time + "红球是:" + red_ball + " 蓝球:" + blue_ball) cursor.execute(sql, [data_time, red_ball, blue_ball]) client.commit()
    更新数据这部分从数据库中获取数据,然后反转顺序,index作为计数器,循环遍历假如有新数据要更新,那result返回值为0,index+1。
    # 查看数据是否存在select_new_sql = "select * from doubleballs where date_time = %s"date_times.reverse()# 记录有多少条新数据index = 0for data_time in date_times: result = cursor.execute(select_new_sql, [data_time]) # 判断数据是否存在 if result == 1: break index+=1
    数据顺序反转,按照网站上xpath写法获取第1个数据,即最新的数据放到数据库中。
    # 数据从新到旧排序trs.reverse()for i in range(index): # 提取红球 red_ball = '-'.join(trs[i].xpath('./td[@class="chartBall01"]/text()')) # 提取蓝球 blue_ball = trs[i].xpath('./td[@class="chartBall02"]/text()')[0] print("第" + date_times[i] + "红球是:" + red_ball + " 蓝球:" + blue_ball) cursor.execute(sql, [date_times[i], red_ball, blue_ball]) client.commit()
    完整代码
    import requestsfrom fake_useragent import UserAgentfrom lxml import htmlimport pymysqletree = html.etree# 彩票数据所在的urlurl = 'http://datachart.500.com/ssq/'# 提取数据response = requests.get(url, headers={"User-Agent": UserAgent().chrome})# 通过xpath去解析e = etree.HTML(response.text)date_times = e.xpath('//tbody[@id="tdata"]/tr/td[1]/text()')trs = e.xpath('//tbody[@id="tdata"]/tr[not(@class)]')# 链接数据库client = pymysql.connect(host='localhost', port=3306, user='root', password='123456', charset='utf8', db='bangumi')cursor = client.cursor()# 插入数据的sqlsql = 'insert into doubleballs values(0,%s,%s,%s)'# 查看数据是否存在select_new_sql = "select * from doubleballs where date_time = %s"date_times.reverse()# 记录有多少条新数据index = 0for data_time in date_times: reslut = cursor.execute(select_new_sql, [data_time]) # 判断数据是否存在 if reslut == 1: break index += 1# 数据从新到旧排序trs.reverse()for i in range(index): # 提取红球 red_ball = '-'.join(trs[i].xpath('./td[@class="chartBall01"]/text()')) # 提取蓝球 blue_ball = trs[i].xpath('./td[@class="chartBall02"]/text()')[0] print("第" + date_times[i] + "红球是:" + red_ball + " 蓝球:" + blue_ball) cursor.execute(sql, [date_times[i], red_ball, blue_ball]) client.commit()# for data_time, tr in zip(date_times, trs):# red_ball = '-'.join(tr.xpath('./td[@class="chartBall01"]/text()'))# blue_ball = tr.xpath('./td[@class="chartBall02"]/text()')[0]# print("第" + data_time + "红球是:" + red_ball + " 蓝球:" + blue_ball)# cursor.execute(sql, [data_time, red_ball, blue_ball])# client.commit()cursor.close()client.close()
    这样排序会让更新之后最新的在数据最后,但是一开始排序的时候不会出现问题,是从新到旧的排序。于是因为有点强迫症,最后还是改成从旧到新排序。只需要改动几行代码即可,反转数据在提取红球蓝球数据前,保证数据最新,判断时每次存在就让index+1,之后加入index = count - index其中count = data_time.__len__()。接着将循环中的i变成index-i-1。最终代码如下,
    import requestsfrom fake_useragent import UserAgentfrom lxml import htmlimport pymysqletree = html.etree# 彩票数据所在的urlurl = 'http://datachart.500.com/ssq/'# 提取数据response = requests.get(url, headers={"User-Agent": UserAgent().chrome})# 通过xpath去解析e = etree.HTML(response.text)date_times = e.xpath('//tbody[@id="tdata"]/tr/td[1]/text()')trs = e.xpath('//tbody[@id="tdata"]/tr[not(@class)]')count =date_times.__len__()# 链接数据库client = pymysql.connect(host='localhost', port=3306, user='root', password='123456', charset='utf8', db='bangumi')cursor = client.cursor()# 插入数据的sqlsql = 'insert into doubleballs values(0,%s,%s,%s)'# 查看数据是否存在select_new_sql = "select * from doubleballs where date_time = %s"# date_times.reverse()# 记录有多少条新数据index = 0for data_time in date_times: reslut = cursor.execute(select_new_sql, [data_time]) # 判断数据是否存在 if reslut == 1: index += 1index = count - index# 数据从新到旧排序# trs.reverse()date_times.reverse()trs.reverse()for i in range(index): # 提取红球 red_ball = '-'.join(trs[index-i-1].xpath('./td[@class="chartBall01"]/text()')) # 提取蓝球 blue_ball = trs[index-i-1].xpath('./td[@class="chartBall02"]/text()')[0] print("第" + date_times[index-i-1] + "红球是:" + red_ball + " 蓝球:" + blue_ball) cursor.execute(sql, [date_times[index-i-1], red_ball, blue_ball]) client.commit()# for data_time, tr in zip(date_times, trs):# red_ball = '-'.join(tr.xpath('./td[@class="chartBall01"]/text()'))# blue_ball = tr.xpath('./td[@class="chartBall02"]/text()')[0]# print("第" + data_time + "红球是:" + red_ball + " 蓝球:" + blue_ball)# cursor.execute(sql, [data_time, red_ball, blue_ball])# client.commit()cursor.close()client.close()
    1 留言 2021-06-09 13:49:20 奖励36点积分
  • 使用VS2013实现对Excel表格的读写 精华

    背景有一天,一位网友加入了我的Q群,然后又通过Q群私信我,向我请教如何使用VS读写excel文件表格的问题。其实,在ta向我请教的时候,我也没有写过这样功能模块或是开发过类似的小程序。但,仍是被ta求知的行为感动了,所以决定花些时间去了解这方面的知识,给ta一个答复。
    于是,经过搜索,找到了相关资料,并写了个示例小程序给ta。当然,那个示例小程序并不是本文给的这个程序,本文的示例小程序是为了配合本文的演示,而故意修改的,更适合初学者使用,两者原理和实现上基本上是一样的。
    现在,我就把这个小程序的实现思路和实现过程,写成文档,分享给大家。
    导入操作EXCEL所需的MFC类库我们需要导入MFC类库,来帮助我们实现对EXCEL表格的操作。那么,就要求我们的项目工程支持MFC。类似,WIN32 控制台程序默认是不支持MFC的,所以,在创建项目的时候,要选择支持MFC。
    现在,本文以WIN32 控制台项目工程为例,讲解导入EXCEL所需类库的操作:
    首先,在“Win32应用程序向导”窗口中,注意要选择“添加公共头文件以用于:MFC”。然后点击“完成”,成功创建项目工程。

    进入项目工程中,选中项目,鼠标右键选择“添加” —> “添加类”。在“添加类”对话框中选择“TypeLib中的MFC类”,即基于类型库添加Microsoft基础类库类。

    接着需要选择OLE/COM 组件的路径,也就是你计算机上excel.exe 所在的路径。我的Microsoft Office是安装在F盘,所以excel.exe路径就是:

    F:\Program Files (x86)\Microsoft Office\Office12\EXCEL.EXE
    路径选择完毕后,需要向项目工程中添加基本的 7 个类( Excel 作为 OLE/COM 库插件,定义好了各类交互的接口,这些接口是跨语言的接口。 VC 可以通过导入这些接口,并通过 接口来对 Excel 的操作), 由于本文只关心对 Excel 表格中的数据的读取,主要关注 7 个接口:_Application、Workbooks、_Workbook、Worksheets、_Worksheet、Range、Font。
    添加完毕后,点击“完成”,即可成功添加 7 个类到项目工程中。

    成功添加 7 个类之后,项目工程会新增 7 个类库的头文件:

    但是,如果我们直接编译项目工程的话,会报错的。所以,现在需要对上述生成的 7 个头文件进行修改:
    将每个头文件顶头的:

    “#import “F:\Program Files (x86)\Microsoft Office\Office12\EXCEL.EXE” no_namespace”
    注释掉。并添加头文件:”#include <afxdisp.h>“

    修改完毕后,再编译程序,若报错,而且错误号为“C2059”,则双击错误,跳转到错误代码行。然后将 将VARIANT DialogBox() 改成 VARIANT _DialogBox() ,再次编译,即可编译通过。


    实现原理从EXCEL表格中读取数据
    首先,使用CApplication::CreateDispatch创建Excel.Application对象,并获取工作簿CWorkbooks
    接着,使用CWorkbooks::Open打开excel表格文件,并获取工作表对象CWorksheets
    然后使用CWorksheets::get_Item获取指定的工作表对象CWorksheet。本文是获取第 1 张工作表
    接着,我们可以调用CWorksheet::get_Range获取读取表格的范围。本文是获取 A1—A1 范围的表格
    然后,对表格内容弹窗输出
    最后,关闭对象,进行清理工作

    向EXCEL表格中写入数据
    首先,使用CApplication::CreateDispatch创建Excel.Application对象,并获取工作簿CWorkbooks
    接着,使用CWorkbooks::Add新添加一个工作簿,并使用CWorkbook::get_Worksheets获取工作表对象
    然后使用CWorksheets::get_Item获取指定的工作表对象CWorksheet。本文是获取第 1 张工作表
    接着,我们可以调用CWorksheet::get_Range获取表格的范围。本文是获取 A1—C3 范围的表格。并调用CRange::put_Value2将表格写入数据,并设置字体以及列宽
    然后,调用CWorkbook::SaveAs保存文件
    最后,关闭对象,进行清理工作

    编码实现从EXCEL表格中读取数据// 读取BOOL MyExcel::ReadExcel(){ //导入 COleVariant covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR); if (!app.CreateDispatch(_T("Excel.Application"))) { ::MessageBox(NULL, "无法创建Excel应用!", "WARNING", MB_OK); return TRUE; } books = app.get_Workbooks(); //打开Excel,其中pathname为Excel表的路径名 lpDisp = books.Open(_T("C:\\Users\\DemonGan\\Desktop\\test.xlsx"), covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional, covOptional); book.AttachDispatch(lpDisp); sheets = book.get_Worksheets(); sheet = sheets.get_Item(COleVariant((short)1)); //获得坐标为(A,1) -- (A,1)的单元格 range = sheet.get_Range(COleVariant(_T("A1")), COleVariant(_T("A1"))); //获得单元格的内容 COleVariant rValue; rValue = COleVariant(range.get_Value2()); //转换成宽字符 rValue.ChangeType(VT_BSTR); //转换格式,并弹窗输出 ::MessageBox(NULL, CString(rValue.bstrVal), "RESULT", MB_OK); book.put_Saved(TRUE); // 退出 app.Quit(); app.ReleaseDispatch(); app = NULL; return TRUE;}
    向EXCEL表格中写入数据// 写入BOOL MyExcel::WriteExcel(){ //导出 COleVariant covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR); if (!app.CreateDispatch(_T("Excel.Application"))) { ::MessageBox(NULL, "无法创建Excel应用!", "WARNING", MB_OK); return TRUE; } books = app.get_Workbooks(); book = books.Add(covOptional); sheets = book.get_Worksheets(); sheet = sheets.get_Item(COleVariant((short)1)); //获得坐标为(A,1)和(C,3)范围区域的9个单元格 range = sheet.get_Range(COleVariant(_T("A1")), COleVariant(_T("C3"))); //设置单元格类容为World Of Demon range.put_Value2(COleVariant(_T("CDIY"))); //选择整列,并设置宽度为自适应 cols = range.get_EntireColumn(); cols.AutoFit(); //设置字体为粗体 font = range.get_Font(); font.put_Bold(COleVariant((short)TRUE)); //获得坐标为(D,4)单元格 range = sheet.get_Range(COleVariant(_T("D4")), COleVariant(_T("D4"))); //设置公式“=RAND()*100000” range.put_Formula(COleVariant(_T("=RAND()*100000"))); //设置数字格式为货币型 range.put_NumberFormat(COleVariant(_T("$0.00"))); //选择整列,并设置宽度为自适应 cols = range.get_EntireColumn(); cols.AutoFit(); //显示Excel表// app.put_Visible(TRUE);// app.put_UserControl(TRUE); // 保存excel表 COleVariant vTrue((short)TRUE), vFalse((short)FALSE), vOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR); COleVariant vFileName(_T("C:\\Users\\DemonGan\\Desktop\\test.xlsx")); book.SaveAs( vFileName, //VARIANT* FileName vOptional, //VARIANT* FileFormat vOptional, //VARIANT* LockComments vOptional, //VARIANT* Password vOptional, //VARIANT* AddToRecentFiles vOptional, //VARIANT* WritePassword 0, //VARIANT* ReadOnlyRecommended vOptional, //VARIANT* EmbedTrueTypeFonts vOptional, //VARIANT* SaveNativePictureFormat vOptional, //VARIANT* SaveFormsData vOptional, //VARIANT* SaveAsAOCELetter vOptional //VARIANT* ReadOnlyRecommended ); // 退出 app.Quit(); app.ReleaseDispatch(); app = NULL; return TRUE;}
    程序测试在 main 函数中调用上述封装好的函数,进行测试。 main 函数为:
    int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]){ int nRetCode = 0; HMODULE hModule = ::GetModuleHandle(NULL); if (hModule != NULL) { // 初始化 MFC 并在失败时显示错误 if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0)) { // TODO: 更改错误代码以符合您的需要 _tprintf(_T("错误: MFC 初始化失败\n")); nRetCode = 1; } else { // TODO: 在此处为应用程序的行为编写代码。 MyExcel myExcel; // 写入数据 myExcel.WriteExcel(); printf("Write OK.\n"); system("pause"); // 读取数据 myExcel.ReadExcel(); printf("Read OK.\n"); system("pause"); } } else { // TODO: 更改错误代码以符合您的需要 _tprintf(_T("错误: GetModuleHandle 失败\n")); nRetCode = 1; } return nRetCode;}
    测试结果
    运行程序,提示写入EXCEL表格成功。

    然后,打开生成的“test.xlsx”文件,数据被成功写入。

    然后,我们继续执行程序,EXCEL表格中的“A1”个的数据成功读取,并弹窗显示。

    总结这个小程序,主要是前期创建工程的时候需要注意,如果你创建的是MFC,那么就跟着上述步骤,导入操作EXCEL所需的MFC类库。但,如果你创建的是其他工程,例如Win32工程,那么在创建的过程中,就应该选择包含MFC的功能,因为程序需要导入操作EXCEL所需的MFC类库,所以工程必须要支持MFC。
    5 留言 2018-12-26 12:56:58 奖励15点积分
显示 0 到 15 ,共 15 条

热门回复

eject